iPhotoライブラリをDropboxで管理するときに気をつけるべきたったひとつのこと

iPhoto Libraryの中にあるOriginal、Data、Modifiedというエイリアスを削除してから共有しましょう。

なんで?

Dropboxエイリアス経由のパスであっても気にせずに物理的にサーバ上にファイルを格納するので、画像が多重にアップロードされてしまい、サーバ利用量がヤバいことになります。というか、なった。

具体的にはどうする?

iPhoto Libraryを右クリックのアレで開いて、Original、Data、Modifiedというエイリアスを削除しましょう。Data.noindexというのがあったらそれも*1iPhotoはこれらがなくても普通に動作しますが、万が一何かあっても自己責任でお願いします。

経緯

元々20GBも使ってないときに、40GBくらいのiPhoto Libraryを共有したら、アップロードに2〜3日かかったあと、合計で105GB使ってますよという状態になってしまったので、有料会員の特権(?)でサポートに問い合わせてみたら、FAQらしくて手順書ページを紹介されました。 対処後に一晩放っておいたら、無事に60GBぐらいに落ち着いて、サイズ保存則が無事に成立しました。

*1:自分はなかった

Gradle1.6の新機能mustRunAfterでタスク間の実行順序が制御可能になった #gradle

Gradleではタスク間の依存関係の指定ができます。

build.gradle:

task a << {
    println "A"
}
task b(dependsOn: "a") << {
    println "B"
}
task c(dependsOn:"a") << {
    println "C"
}
task d(dependsOn: ["b", "c"]) << {
    println "D"
}

実行結果:

$ gradle d
:a
A
:b
B
:c
C
:d
D

BUILD SUCCESSFUL

Total time: 1.612 secs

実はこの例のBとCの間の関係は不定であり、実装に基づいて(この場合はアルファベット順だったりしたはず)実行順序が決められます。 ここではA→B→C→Dになっていますね。

しかし、このようなグラフ的には一見どっちがさきに実行されても良いようにみえるBとCの間に、実は微妙な前後関係があって、Cの方が先に実行されて欲しい!というケースは意外とありますよね。

長い間Gradleユーザから臨まれていたこの機能がついに1.6で実装されました(ということをさっき知った)!

Task#mustRunAfter()の使い方

以下のように使います。

build.gradle:

task a << {
    println "A"
}
task b(dependsOn: "a"/*, mustRunAfter: "c" -- 残念ながら今のところここには書けないらしい*/) {
    mustRunAfter "c" // メソッド呼出として書く
    doLast {
        println "B"
    }
}
task c(dependsOn:"a") << {
    println "C"
}
task d(dependsOn: ["b", "c"]) << {
    println "D"
}

と書くと、こうなります:

$ gradle d
:a
A
:c
C
:b
B
:d
D

BUILD SUCCESSFUL

Total time: 1.64 secs

dependsOnとの違い

実は単にA→C→B→Dとしたいだけであれば、↓と書くだけでもOKです。

task a << {
    println "A"
}
task b(dependsOn: ["a", "c"]) << {
    println "B"
}
task c(dependsOn:"a") << {
    println "C"
}
task d(dependsOn: ["b", "c"]) << {
    println "D"
}

mustRunAfterとの違いは、タスクbを実行してみると見えてきます。 一つ前のサンプルだと:

$ gradle b
:a
A
:c
C
:b
B

BUILD SUCCESSFUL

Total time: 0.723 secs

となります。 bの依存先にcが入ってるので実行タスクのグラフにcが登場するのですね。

さて、mustRunAfterを使った先ほどと同じこの設定で実行してみると...

task a << {
    println "A"
}
task b(dependsOn: "a") {
    mustRunAfter "c"
    doLast {
        println "B"
    }
}
task c(dependsOn:"a") << {
    println "C"
}
task d(dependsOn: ["b", "c"]) << {
    println "D"
}

実行結果:

$ gradle b
:a
A
:b
B

BUILD SUCCESSFUL

Total time: 1.745 secs

となります。 mustRunAfterは依存関係の指定ではなく、実行タスクのグラフ上にそのタスクが存在した場合に単にその順序関係を指定しているだけなので、この場合にはタスクcは登場しないのです!

実際どういう場面で使うの?

ふもさんのサンプルがわかりやすそうです。

task clean << {
  println 'cleaning.'
}
task build << {
  println 'building.'
}
build.doLast {
  println 'finish!'
}
task local(dependsOn: [clean, build]) << {
  println 'local debug.'
}

これを実行するとこうなります:

$ gradle local
:build
building.
finish!
:clean
cleaning.
:local
local debug.

BUILD SUCCESSFUL

Total time: 1.638 secs

buildしてからcleanするとかso crazyですね。ちょっと何言ってるか分からないです。

ここで実行順序制御にdependsOnを使おうとすると、

task clean << {
  println 'cleaning.'
}
task build(dependsOn: 'clean') << {
  println 'building.'
}
build.doLast {
  println 'finish!'
}
task local(dependsOn: [clean, build]) << {
  println 'local debug.'
}

実行結果:

$ gradle local
:clean
cleaning.
:build
building.
finish!
:local
local debug.

BUILD SUCCESSFUL

Total time: 1.605 secs

うまくいった!?ようにみえますが、cleanせずに差分ビルドさせようとbuildタスクを指定すると...

$ gradle build
:clean
cleaning.
:build
building.
finish!

BUILD SUCCESSFUL

Total time: 0.755 secs

cleanまで発動してしまいました!!これでは毎回フルビルドになってしまい、開発速度が上がりません。

そこで、mustRunAfterの出番です。

build.gradle:

task clean << {
  println 'cleaning.'
}
task build {
  mustRunAfter 'clean'
  doLast {
    println 'building.'
  }
}
build.doLast {
  println 'finish!'
}
task local(dependsOn: [clean, build]) << {
  println 'local debug.'
}

実行結果:

$ gradle local
:clean
cleaning.
:build
building.
finish!
:local
local debug.

BUILD SUCCESSFUL

Total time: 0.722 secs
$ gradle build
:build
building.
finish!

BUILD SUCCESSFUL

Total time: 1.58 secs

素晴らしい!期待通りの結果です。

というわけで、依存関係のdependsOnと、実行順序の微調整のmustRunAfterをうまく使い分けましょう。

コペンハーゲンのGR8Conf EU 2013でGroovyServとImproxプラグインを紹介してきた

https://pbs.twimg.com/media/BLDRxqACQAE4lf1.jpg

5/22-24にデンマークはコペンハーゲンで開催されたGR8Conf EU 2013というGroovy系のカンファレンスで、GroovyServImproxプラグインを紹介してきました。

右の写真はコペンハーゲンの定番がっかり観光スポットの「人魚の像」ですね。 がっかり感が軽減されている一枚を選びました。 まあ、どこの観光地にもがっかりスポットはつきものです。 札幌の時計台にくらべれば人魚はだいぶ良かったんじゃないでしょうか。

GR8Conf EUは今年で5周年だそうです。コペンハーゲンがオリジナルですが、「GR8Conf」ブランドのカンファレンスは別ロケーションでも何度か開かれていて、USは今年も7月にありますしほぼレギュラー化してるようですね。他に覚えているところではオーストラリアでも開催されてました。日本でも開きたいですね。

さて、自分の発表ですが、本日動画が公開され(てしまっ)たので、スライドと動画のリンクを張っておきます。 つたない英語ですがよければご笑覧ください。

当方の深刻な英語力不足にともない今回は不本意ながら「最初に言い訳」メソッドで乗り切りましたが、やっぱりせっかくやるからには「ネイティブ&インタラクティブ」方式を目指したいですね。 最後、質疑がなくて寂しい感じにみえますが、たぶんみなさん気を遣ってくれた感じで、セッション後に数名の方と技術的な話もしましたからね? *1

他の錚々たるスピーカのみなさんの動画も随時公開されていくはずなので、興味がある方はTwitter@gr8confをフォローしておくと良いと思います。

そうそう、今度の6/21のJGGUG主催GワークショップZで、G*ワークショップZ Jun 2013 - GR8Conf報告&ライブコーディング&GroovyServ/Improxをやります。 ご興味があれば是非是非ご参加ください。

*1:どちらかというと「お前の英語は悪くない」「問題ないよ」「グレートだったぞ」みたいな慰めにきてくれた人の方が多かったですけど。

GrailsからMirageを使うサンプルがあったので試してみた

Mirageという2-way SQLが利用できる素敵なO/RマッパをGrailsで使ってみたというブログがあったので、サンプルコードを落として試してみました。

developmentモードでMySQL使うようになってたので、H2に変更してrun-appして試してみました。

確かにMirageでDB操作できている!

のですが、更新ボタン2回目を押すとuniqueエラーになるのでおかしいなと思ってみてみたら、insert.sqlファイル内に書いてあるINSERT文でIDがハードコードされてるのでそりゃ2回目は失敗しますね。idを省略して自動払い出しを期待して実行してみると、実行毎にレコードが追加できます。素晴らしい。

という感じで、コードを色々見ていたところ、気になる点が色々あったので、forkして自分好みに修正してみました。 コネクション・データソース・トランザクションまわりのための準備が煩雑だったので、その辺を重点的に頑張ってみました。これ、以外と難しくて一応うまく動いてるようだけどもそんなに自信ないです。たぶん大丈夫だと思うんだけど。 あと、テストもSpockで追加したり、もちろんimproxプラグインも追加したりして、かなり好き放題いじってます。 Grailsのバージョンも最新の2.2.2にあげちゃいました。

コミットログに日本語でゆるいコメントを残してるので何をしていったかはコミットログ参照のこと。コミットログとdiffみたらだいたい何やったか理解できるんじゃないかと。

HibernateのネイティブSQLやGroovy-SQLを使う方法で間に合うことも多いですが、2-way SQLを外部ファイルで指定できるのはSeasarの追っかけ的には嬉しいので、チャンスがあったら使ってみようかなぁ。GrailsConnectionProviderの件とかもあるのでプラグイン化も検討してみようかしら。

参考URL

GrailsのGORMにおける各種関連とmappingによる名前変更のサンプル

Grailsのドキュメントを翻訳する上で、英文読んでも何を言ってるか分からないので、サンプル書いて試してみた記録です。

↓ここに反映される予定。

生成されたスキーマPostgreSQLにて確認してあります。 異なるDBMSなら異なるスキーマになることもあるかも知れません。Hibernateのみぞ知る。

多対1/1対1関連(単方向)

基本

ドメインクラス

class Person {

    String firstName
    Address address

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
    }
}
class Address {

    String number
    String postCode

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 address_id | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fkc4e2328f2efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "people" CONSTRAINT "fkc4e2328f2efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

Person上のaddressカラムのマッピングを変更してみる

ドメインクラス

class Person {

    String firstName
    Address address

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        address column: 'Person_Address_Id' // コレの効果は...?
    }
}
class Address {

    String number
    String postCode

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

                 Table "public.people"
      Column       |          Type          | Modifiers
-------------------+------------------------+-----------
 id                | bigint                 | not null
 version           | bigint                 | not null
 person_address_id | bigint                 | not null  <-------- ココが変わった!!!
 first_name        | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fkc4e2328fec329d0d" FOREIGN KEY (person_address_id) REFERENCES address(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "people" CONSTRAINT "fkc4e2328fec329d0d" FOREIGN KEY (person_address_id) REFERENCES address(id)

多対1/1対1関連(双方向)

基本

ドメインクラス

class Person {

    String firstName
    Address address

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
    }
}
class Address {

    String number
    String postCode

    static belongsTo = [person:Person]

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

単方向の場合とスキーマは同じ。

              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 address_id | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fkc4e2328f2efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Referenced by:    TABLE "people" CONSTRAINT "fkc4e2328f2efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

Person上のaddressカラムのマッピングを変更してみる

単方向の場合とスキーマは同じなので、後は...わかりますね?

1対多関連(単方向)

基本

ドメインクラス

class Person {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
    }
}
class Address {

    String number
    String postCode

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "people_address" CONSTRAINT "fk3af1764434f4e10e" FOREIGN KEY (person_addresses_id) REFERENCES people(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "people_address" CONSTRAINT "fk3af176442efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

      Table "public.people_address"
       Column        |  Type  | Modifiers
---------------------+--------+-----------
 person_addresses_id | bigint |
 address_id          | bigint |
Foreign-key constraints:
    "fk3af176442efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)
    "fk3af1764434f4e10e" FOREIGN KEY (person_addresses_id) REFERENCES people(id)

Person上のaddressesカラムのマッピングを変更してみる

ドメインクラス

class Person {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        addresses column: 'Person_Address_Id' // コレの効果は...?
    }
}
class Address {

    String number
    String postCode

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:    "people_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "people_address" CONSTRAINT "fk3af17644e3b329fc" FOREIGN KEY (person_address_id) REFERENCES people(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "people_address" CONSTRAINT "fk3af176442efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

     Table "public.people_address"
      Column       |  Type  | Modifiers
-------------------+--------+-----------
 person_address_id | bigint |              <-------- ココが変わった!!!
 address_id        | bigint |
Foreign-key constraints:
    "fk3af176442efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)
    "fk3af17644e3b329fc" FOREIGN KEY (person_address_id) REFERENCES people(id)

関連テーブル自体を指定する

ドメインクラス

class Person {
    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        addresses joinTable: [name: 'Person_Addresses',
                              key: 'Person_Id',
                              column: 'Address_Id'] // コレの効果は...?
    }
}
class Address {

    String number
    String postCode

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "person_addresses" CONSTRAINT "fk8aeab389671fd1" FOREIGN KEY (person_id) REFERENCES people(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "person_addresses" CONSTRAINT "fk8aeab382efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)

 Table "public.person_addresses"   <-------- ココが変わった!!!
   Column   |  Type  | Modifiers
------------+--------+-----------
 person_id  | bigint | not null    <-------- ココが変わった!!!
 address_id | bigint |
Foreign-key constraints:
    "fk8aeab382efad9e3" FOREIGN KEY (address_id) REFERENCES address(id)
    "fk8aeab389671fd1" FOREIGN KEY (person_id) REFERENCES people(id)

1対多関連(双方向)

基本

ドメインクラス

class Person {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        addresses cascade: "all-delete-orphan"
    }
}
class Address {

    String number
    String postCode

    static belongsTo = [person:Person]

    static mapping = {
        postCode type: 'text'
    }
}

生成されたスキーマ

関連テーブルなしに、子テーブル側に親へのIDを保持するスキーマとなる。 「双方向」であるため、晴れて子から親を参照してもOKになるので、関連テーブルを導入するまでもなく、単に親 のIDを持つスキーマ構造が採用できる、ということと推測するが...。

              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "address" CONSTRAINT "fkbb979bf49671fd1" FOREIGN KEY (person_id) REFERENCES people(id)

             Table "public.address"
  Column   |          Type          | Modifiers
-----------+------------------------+-----------
 id        | bigint                 | not null
 version   | bigint                 | not null
 number    | character varying(255) | not null
 person_id | bigint                 | not null
 post_code | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fkbb979bf49671fd1" FOREIGN KEY (person_id) REFERENCES people(id)

Person上のaddressesカラムのマッピングを変更してみる

ドメインクラス

class Person {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        addresses cascade: "all-delete-orphan",
                  column: 'Person_Address_Id' // こっちに書いても効かない(エラーにならず単に無視された)
    }
}
class Address {

    String number
    String postCode

    static belongsTo = [person:Person]

    static mapping = {
        postCode type: 'text'
        person column: "my_person_id" // コレの効果は...?
    }
}

生成されたスキーマ

hoge=# \d people              Table "public.people"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "address" CONSTRAINT "fkbb979bf4576400fe" FOREIGN KEY (my_person_id) REFERENCES people(id)

hoge=# \d address
              Table "public.address"
    Column    |          Type          | Modifiers
--------------+------------------------+-----------
 id           | bigint                 | not null
 version      | bigint                 | not null
 number       | character varying(255) | not null
 my_person_id | bigint                 | not null <-------- ココが変わった!!!
 post_code    | text                   | not null
Indexes:
    "address_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fkbb979bf4576400fe" FOREIGN KEY (my_person_id) REFERENCES people(id)

多対多関連(双方向)

基本

ドメインクラス

class Group {

    String name

    static hasMany = [people: Person]

    static mapping = {
       table 'my_group' // PostgreSQLでは'group'テーブルを作成しようとするとエラーになる
    }
}
class Person {
    String firstName

    static hasMany = [groups: Group]
    static belongsTo = Group
}

生成されたスキーマ

           Table "public.my_group"
 Column  |          Type          | Modifiers
---------+------------------------+-----------
 id      | bigint                 | not null
 version | bigint                 | not null
 name    | character varying(255) | not null
Indexes:    "my_group_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "my_group_people" CONSTRAINT "fk7525b002f314e043" FOREIGN KEY (group_id) REFERENCES my_group(id)

              Table "public.person"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not nullIndexes:
    "person_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "my_group_people" CONSTRAINT "fk7525b0029671fd1" FOREIGN KEY (person_id) REFERENCES person(id)

 Table "public.my_group_people"
  Column   |  Type  | Modifiers
-----------+--------+-----------
 group_id  | bigint | not null
 person_id | bigint | not null
Indexes:
    "my_group_people_pkey" PRIMARY KEY, btree (group_id, person_id)
Foreign-key constraints:
    "fk7525b0029671fd1" FOREIGN KEY (person_id) REFERENCES person(id)
    "fk7525b002f314e043" FOREIGN KEY (group_id) REFERENCES my_group(id)

外部キーカラム名を変更する

ドメインクラス

class Group {

    String name

    static hasMany = [people: Person]

    static mapping = {
       table 'my_group'
       people column: 'Group_Person_Id' // コレの効果は...?
    }
}
class Person {
    String firstName

    static hasMany = [groups: Group]
    static belongsTo = Group

    static mapping = {
       groups column: 'Group_Group_Id' // コレの効果は...?
    }
}

生成されたスキーマ

           Table "public.my_group"
 Column  |          Type          | Modifiers
---------+------------------------+-----------
 id      | bigint                 | not null
 version | bigint                 | not null name    | character varying(255) | not null
Indexes:
    "my_group_pkey" PRIMARY KEY, btree (id)Referenced by:
    TABLE "my_group_people" CONSTRAINT "fk7525b0027e1c21ed" FOREIGN KEY (group_person_id) REFERENCES my_group(id)

              Table "public.person"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:
    "person_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "my_group_people" CONSTRAINT "fk7525b00253631627" FOREIGN KEY (group_group_id) REFERENCES person(id)

    Table "public.my_group_people"
     Column      |  Type  | Modifiers
-----------------+--------+-----------
 group_person_id | bigint | not null <-------- ココが変わった!!!
 group_group_id  | bigint | not null <-------- ココが変わった!!!
Indexes:
    "my_group_people_pkey" PRIMARY KEY, btree (group_person_id, group_group_id)
Foreign-key constraints:
    "fk7525b00253631627" FOREIGN KEY (group_group_id) REFERENCES person(id)
    "fk7525b0027e1c21ed" FOREIGN KEY (group_person_id) REFERENCES my_group(id)

関連テーブル名を指定する

ドメインクラス

class Group {

    String name

    static hasMany = [people: Person]

    static mapping = {
       table 'my_group'
       people column: 'Group_Person_Id',
              joinTable: 'PERSON_GROUP_ASSOCIATIONS' // コレの効果は...?
    }
}
class Person {
    String firstName

    static hasMany = [groups: Group]
    static belongsTo = Group

    static mapping = {
       groups column: 'Group_Group_Id',
              joinTable: 'PERSON_GROUP_ASSOCIATIONS' // コレの効果は...?
    }
}

生成されたスキーマ

           Table "public.my_group"
 Column  |          Type          | Modifiers
---------+------------------------+-----------
 id      | bigint                 | not null
 version | bigint                 | not null
 name    | character varying(255) | not null
Indexes:    "my_group_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "person_group_associations" CONSTRAINT "fk9dd6e6fc7e1c21ed" FOREIGN KEY (group_person_id) REFERENCES my_group(id)

              Table "public.person"
   Column   |          Type          | Modifiers
------------+------------------------+-----------
 id         | bigint                 | not null
 version    | bigint                 | not null
 first_name | character varying(255) | not null
Indexes:    "person_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "person_group_associations" CONSTRAINT "fk9dd6e6fc53631627" FOREIGN KEY (group_group_id) REFERENCES person(id)

Table "public.person_group_associations" <-------- ココが変わった!!!
     Column      |  Type  | Modifiers
-----------------+--------+-----------
 group_group_id  | bigint | not null
 group_person_id | bigint | not null
Indexes:
    "person_group_associations_pkey" PRIMARY KEY, btree (group_person_id, group_group_id)
Foreign-key constraints:
    "fk9dd6e6fc53631627" FOREIGN KEY (group_group_id) REFERENCES person(id)
    "fk9dd6e6fc7e1c21ed" FOREIGN KEY (group_person_id) REFERENCES my_group(id)

GExcelAPI 0.3 リリースしました #gexcelapi

GExcelAPI 0.3をリリースしました。

Change Log、といえるかどうか

変更点としては、以下の2点。

これだけ?

はい、これだけです。

でも、Java7対応重要です。ですよね?

オレオレMavenリポジトリの移転

あと、今後の使うときの注意点として大事なのが、オレオレMavenリポジトリの移転です。 今までMavenリポジトリとしてGitHubのrawモードを使った疑似リポジトリを使っていましたが、今回からCloudBeesのリポジトリを使うようにしてみました。 既存の0.1、0.2もなんとなくそっちにコピーしてありますので、ひとまず今後はCloudBeesをお使いください*1

release (stableバージョン)
    https://repository-kobo.forge.cloudbees.com/release
snapshot (SNAPSHOTバージョン)
    https://repository-kobo.forge.cloudbees.com/snapshot

ごくシンプルなサンプルコードはこんな感じ:

@GrabResolver(name="kobo", root="https://repository-kobo.forge.cloudbees.com/release")
@Grab("org.jggug.kobo:gexcelapi:0.3")
import org.jggug.kobo.gexcelapi.GExcel

def book = GExcel.open(args[0])

assert book.sheets[0].A1.value == "Sheet1-A1"

機能的に網羅したサンプルコードはテストコードを参照のこと:

CellRange#toHtml()の対応状況?

あと、以前に紹介した実験的機能としてのCellRange#toHtml()は以前の通り実験的機能としての位置づけでそのまま含まれています。 シンプルではないシートの場合にまともに動かないことも多いかもしれませんが、実験的と言うことでご了承ください。 なお、パッチ等は大歓迎です。 というか、Windows上のExcelを使う機会が大変少なくなっている昨今なので、むしろコミッタ募集です。

*1:そのうちまた変わるかもしれませんが

IntelliJ IDEAの細かすぎてわかりづらいGroovyサポート:メソッドシグネチャのホバー表示

メソッドコール部分をホバーするとAPIドキュメントを表示してくれるのはIDEとして普通のことだけど、 Groovyのような言語ではメソッドシグネチャで型を明示指定してないことが良くあります。 そういう場合でも、呼出先で使われるプロパティやメソッドなどを使ってどんなオブジェクトを期待しているか、可能な限りわかりやすく表示してくれる。

と、言葉で書いても何を言ってるかわからないだろうから、例を書くと

def hoge(foo, bar, String baz) {
    foo.name = ""
    println foo.value
    bar.toString()
    baz.toString()
}

def zzzz() {
    hoge()
}

というメソッドがあるときに、zzzzのhoge()の上でカーソルをホバーしてみると(自分の環境だとCommand押しながらホバーでした)....

String hoge (foo.[name, value], bar.toString(), String baz)

と表示されます。

わかります?

  • fooはnameとvalueというプロパティが使われてるので、そういうオブジェクト渡せよ
  • barはtoString()メソッドが呼ばれてるので、そういうオブジェクト渡せよ
  • bazはString型のインスタンス渡せよ

ということが分かります。

長いメソッドだと色んなプロパティ・メソッドが列挙されすぎてすごいことになるんじゃないか!?と心配になりますが、さすがに3つめあたりで省略記法になるようです。

Object hoge (foo.[name, value, ...], bar.toString(), String baz)

インテリジェイアイデアすごい。