tokuhirom's blog

Karabiner の private.xml を設定した

Realforce 買ったので private.xml を書いた。

private.xml を書くと、MBP 本体のキーボードと外付けキーボードとの間で別の設定ができるようになるし、設定を複数の環境で共有するのが楽になるから、書いたほうがよさそう。

今回かいたのは以下の様な感じである。

<?xml version="1.0"?>
<root>
    <appdef>
        <appname>TERMINAL</appname>
        <equal>com.googlecode.iterm2</equal>
    </appdef>

  <devicevendordef>
    <vendorname>RealForce_VendorID</vendorname>
    <vendorid>0x0853</vendorid>
  </devicevendordef>
  <deviceproductdef>
    <productname>RealForce_ProductID</productname>
    <productid>0x0111</productid>
  </deviceproductdef>
  <devicevendordef>
    <vendorname>MBP_VendorID</vendorname>
    <vendorid>0x05ac</vendorid>
  </devicevendordef>
  <deviceproductdef>
    <productname>MBP_ProductID</productname>
    <productid>0x0262</productid>
  </deviceproductdef>

  <item>
    <name>RealForce用セッティング</name>
    <appendix>Swap COMMAND and OPTION</appendix>
    <identifier>private.for_RealForce</identifier>
    <block>
      <device_only>
        DeviceVendor::RealForce_VendorID,
        DeviceProduct::RealForce_ProductID
      </device_only>
      <!-- 左コマンドを左OPTIONに -->
      <autogen>__KeyToKey__ KeyCode::COMMAND_L, KeyCode::OPTION_L</autogen>
      <!-- 右コマンドを右ALTに -->
      <autogen>__KeyToKey__ KeyCode::COMMAND_R, KeyCode::OPTION_R</autogen>
      <!-- 左ALTを左コマンドに、空打ちでEISUU -->
      <autogen>__KeyOverlaidModifier__ KeyCode::OPTION_L, KeyCode::COMMAND_L, KeyCode::JIS_EISUU</autogen>
      <!-- 右ALTを右コマンドに、空打ちでKANA -->
      <autogen>__KeyOverlaidModifier__ KeyCode::OPTION_R, KeyCode::COMMAND_R, KeyCode::JIS_KANA</autogen>
    </block>
  </item>
  <item>
    <name>MBP用セッティング</name>
    <appendix>Swap COMMAND and OPTION</appendix>
    <identifier>private.for_mbp</identifier>
    <block>
      <device_only>
        DeviceVendor::MBP_VendorID,
        DeviceProduct::MBP_ProductID
      </device_only>
      <!-- 左Command空打ちで英数 -->
      <autogen>__KeyOverlaidModifier__ KeyCode::COMMAND_L, KeyCode::COMMAND_L, KeyCode::JIS_EISUU</autogen>
      <!-- 右コマンドを空打ちでかな -->
      <autogen>__KeyOverlaidModifier__ KeyCode::COMMAND_R, KeyCode::COMMAND_R, KeyCode::JIS_KANA</autogen>
    </block>
  </item>

    <item>
        <name>Control+J to Kana</name>
        <identifier>option.kana.control_j</identifier>
        <autogen>__KeyToKey__ KeyCode::J, ModifierFlag::CONTROL_L, KeyCode::JIS_KANA</autogen>
    </item>

</root>
Created: 2014-08-01 17:19:42
Updated: 2014-08-01 17:19:42

Split Amon2::Plugin::Web::CSRFDefender from core distribution.

1. Amon2::Plugin::Web::CSRFDefender was removed from Amon2 core distribution.

Amon2::Plugin::Web::CSRFDefender is no longer default CSRF defender module in Amon2. I suggest to use HTTP::Session2.

If you still use Amon2::Plugin::Web::CSRFDefender, you need to write dependency explicitly in your cpanfile.

2. Latest Amon2::Plugin::Web::CSRFDefender is bit secure.

@mala says Amon2::Util::random_string is bit unsecure for token generation. I change the default token generation algorithm in Amon2::Plugin::Web::CSRFDefender.

I think it's not a critical security issue. But you can update it.

Created: 2014-07-30 17:03:20
Updated: 2014-07-30 17:03:20

Regexp::Trie を java に移植した

https://github.com/tokuhirom/regexp-trie

Regexp::Assemble っぽいことをするには graph-expression に入ってるやつを使えばいい、という話は昨日書いた のですが、生成コードがあまり綺麗ではないし、まあ今どき google-collections に依存しているところからも分かる通り、メンテナンスはされていないようです。

というわけで、@dankogai の Regexp::Trie を java に移植してみました。

使い方は以下の様になります。Perl5 とほぼ同じ感じで使えていますね(例は例によって groovy)。

@GrabResolver(name='tokuhirom', root='https://tokuhirom.github.io/maven/releases/')
@Grab('me.geso:regexp-trie:0.0.1')

import me.geso.regexp_trie.RegexpTrie;

def trie = new RegexpTrie();
["foobar", "fooxar", "foozap", "fooza"].forEach {
    trie.add(it);
}
println(trie.regexp()) // → (?:foo(?:bar|xar|zap?))

依存もないし、シンプルなコードなので使いやすいと思います。

必要な JDK バージョンは 7 です。


コードは Perl 版に比べて長くなっているが、読みやすくなっていると思います。 長くなってる感じがするのは、元のコードが dan さんのコードなので、めっちゃ詰めて書いてあるからというのもでかい。

今回、唯一詰まったのは、

TreeMap<String, TreeMap<String, TreeMap<String, ...>

みたいな再帰的な型をどうやって定義しようかな、というところだったのですが、これは

class CharTrie extends TreeMap<String, CharTrie> { }

とすればよいのでした。

あと、当初は Character 型を Trie のキーにしていたのだが、null は TreeMap の key に使えないということに気づいたので、String 型に変更し、terminator は "" で表現することに変更しました。 (本当は Terminator オブジェクトとツリーオブジェクトを定義する方が Java らしいのかもしれないけど、めんどくさいし今動いてるのでいいということにします。もっと Java らしくてかっこいい設計が思いつく人がいたら教えてほしいです。)

Created: 2014-07-24 07:02:03
Updated: 2014-07-24 07:02:03

groovy の grapes がうまくいかないときのデバッグ方法

groovy -Divy.message.logger.level=4 -Dgroovy.grape.report.downloads=true examples/synopsis.groovy

みたいにすると、ivy のログがめっちゃ出るようになるので、なにが原因だか探しやすくなる。


なお、ivy は dependency resolver で、cpanm の依存解決部分だけをやってくれる機能みたいなライブラリです。

Apache ivy は、単体のライブラリとしても使えるし、ivy.jar を使って ivy.xml からダウンロードさせる、みたいなこともできます。ant と組み合わせる場合には ivy を ant の中に埋め込んだりできる。

Maven が、依存関係の解決とビルドシステムを密結合させているのに対し、Ivy は単体のコンポーネントとして再利用可能なところが Cool。

あと、Apache Ivy は、Gradle の依存解決にも使われています。

Created: 2014-07-24 06:31:08
Updated: 2014-07-24 06:31:08

Java で Regexp::Assemble のように効率的な正規表現を文字列のリストから組み立てる

graph-expression ってやつに入ってる RegExpUtils ってのを使うと出来ます。 (サンプルコードは groovy)

#!/usr/bin/env groovy

@GrabResolver(name='mromll', root='http://mromll.googlecode.com/svn/repo/')
@Grab(group='com.myml', module='gexp', version='0.1')

import com.myml.gexp.chunker.common.util.RegExpUtils;

String [] examples = ['a', 'b', 'c', 'cca'];

String optimizedRegexp = RegExpUtils.convertListToRegexp(true, examples);
println(optimizedRegexp);

出力は以下のようになります。

(?:c(?:ca|)|a|b)

これを使うと、Java でも簡単に効果的な正規表現を生成できます。やったね!

ちなみに開発が終わってるっぽいのと、依存してるモジュールがちょっとおおいので、プロダクションコードの依存に入れるのは微妙だと思うけど、 用途的に実行時に使うものじゃないのでまあいいか、という感じです。

Created: 2014-07-23 13:46:25
Updated: 2014-07-23 13:46:25

nanobench のインターフェース変えた

lambda 使わないほうがいいんじゃない?という意見を kazuho さんから頂いたので、使わないようにしてみた。 lambda 使わない方がデバッガで追いやすいというのは一理あるので、それもまたいいのかな、と。

というわけで、以下のように書けばいいという感じになった。

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;

public class ListBenchmark {
    // Benchmarking method must be started with 'bench'.
    public void benchArrayList() {
        List<Integer> l = new ArrayList<>();
        for (int i=0; i<1_000_000; ++i) {
            l.add(i);
        }
    }

    public void benchLinkedList() {
        List<Integer> l = new LinkedList<>();
        for (int i=0; i<1_000_000; ++i) {
            l.add(i);
        }
    }
}

/^bench/ から始まってるメソッドが走っていい感じにベンチマークが取られます。

これを javac でコンパイルして nanobench.jar から実行させればいいという寸法。

> javac ListBenchmark.java
> java -jar nanobench.jar ListBenchmark

Score:

benchArrayList:  1 wallclock secs ( 1.03 usr +  0.10 sys =  1.13 CPU) @ 142.79/s (n=162)
benchLinkedList:  2 wallclock secs ( 1.07 usr +  0.15 sys =  1.21 CPU) @ 146.54/s (n=178)

Comparison chart:

                      Rate  benchArrayList  benchLinkedList
     benchArrayList  143/s              --              -3%
    benchLinkedList  147/s              3%               --

nanobench.jar があれば、どこでも動くので便利。

Created: 2014-07-15 11:53:31
Updated: 2014-07-15 11:53:31

Java 8 で例外を投げていることをテストしたい

特定の条件で例外を投げるコードを書いたときに、それをテストしたい。なんてときがある。 このクラスの例外があがってるってことだけ判断できればいいんだけどな、ぐらいのゆるい条件のとき。

そんな時は以下のようなユーティリティを定義すればいい。

public static void assertThrows(Class<? extends Exception> exceptionClass, Code code) {
    boolean thrown = false;
    try {
        code.run();
    } catch (Exception ex) {
        System.out.println(ex);
        assertTrue(exceptionClass.isInstance(ex));
        thrown = true;
    }
    assertTrue(thrown);
}

@FunctionalInterface
public interface Code {
    public void run() throws Exception;
}

利用法は以下のようになる。

@Test
public void testDelete() throws SQLException, HanaException {
    Member m1 = new Member().setEmail("foo@example.com").insert(conn);
    Assert.assertNotNull(m1);
    Member m2 = new Member().setEmail("foo@example.com").insert(conn);
    Assert.assertNotNull(m2);
    Follow follow = new Follow()
            .setFromMemberId(m1.getId())
            .setToMemberId(m2.getId())
            .insert(conn);

    assertThrows(HanaNoPrimaryKeyException.class, () -> {
        follow.delete(conn);
    });
}

以前の Java ではこのような簡便な書き方はできなかったが、Java8 では lambda expression を利用して、LL に近い表現が可能になっている。


と、ここまで書いたところで、実は JUnit でできるんじゃないかと思って調べてみたところ、以下のような解決策があることがわかった。


Spring Framework の場合は以下のようにできるようだ http://docs.spring.io/spring/docs/2.5.x/api/org/springframework/test/AssertThrows.html

 public class FooTest {
    public void testSomeBusinessLogicBadArgumentPath() {
        new AssertThrows(IllegalArgumentException.class) {
            public void test() {
                new Foo().someBusinessLogic(null);
            }
        }.runTest();
    }
 }

これは素晴らしい。これならなにも問題がない。ただ、lambda を使ってる場合よりちょっと長いけど、 このサンプルは spring の古いバージョンのドキュメントを見ているのでしょうがないのかもしれない。 Spring の新しいバージョンのドキュメントを調べたいところだが、めんどくさいので無理だった。


また、StackOverflow によれば以下のように JUnit の機能で書けるようだ。 http://stackoverflow.com/questions/156503/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests

@Test(expected=IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

しかしこれは、テストの条件を Annotation に出してしまっていて、非常にわかりづらい。 しらない人が見てもこの annotation のパラメータがテスト条件だとは気づかないだろう。

Created: 2014-07-14 09:49:29
Updated: 2014-07-14 09:49:29

cv ってコマンドが面白い

procfs で他のプロセスの seek 位置をみることで、進捗状況を外からモニタリングするツールとのこと。

https://github.com/Xfennec/cv

Created: 2014-07-14 09:29:02
Updated: 2014-07-14 09:29:02

Lingua::EN::Inflect みたいなのを Java でやるには Evo Inflector を使う

https://github.com/atteo/evo-inflector

    System.out.println(English.plural("word")); // == "words"

とかやればいいので楽。

Created: 2014-07-08 17:43:27
Updated: 2014-07-08 17:43:27

Java8 Stream API では collector とかを複数回呼ぶと実行時エラーになる

以下のようなコードは、実行時エラーになる。

Stream<String> ss = Arrays.asList("hoge", "fuga").stream();
System.out.println(ss.count());
System.out.println(ss.count());

スタックトレースは以下のようになる。

stream has already been operated upon or closed
java.lang.IllegalStateException
    at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
    at java.util.stream.LongPipeline.<init>(LongPipeline.java:91)
    at java.util.stream.LongPipeline$StatelessOp.<init>(LongPipeline.java:572)
    at java.util.stream.ReferencePipeline$5.<init>(ReferencePipeline.java:221)
    at java.util.stream.ReferencePipeline.mapToLong(ReferencePipeline.java:220)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at StreamSampleTest.testStreamTwice(StreamSampleTest.java:21)
    ...

実行時エラーになるのはつらい。慣れてないときはこういうの書いちゃう人大そう。

というようなことがあるので、Stream に対する代入は今後、IDEが警告するようになるんじゃないか?

という話を Java についてカジュアルに語るチャンネルである #java-casual@freenode でしていたというメモ。

Created: 2014-07-08 17:10:22
Updated: 2014-07-08 17:10:22