trait内のクロージャからプライベートメソッドが見えない
Groovy 2.3から導入されたtraitを使っていて、妙な事象に遭遇したのでまとめてみます。具体的には、traitの中でクロージャを使う場合に、クロージャからプライベートメソッドが見えない仕様があるようです。
例えば、下記のような trait T
があるとします。
trait T { private a(x) { "--$x--" } // プライベートメソッドを直接呼んでいる def b(x) { a(x) } // クロージャの中からプライベートメソッドを呼んでいる def c(xs) { xs.collect { x -> a(x) } } }
下記のように、この trait を適用した class P
および class Q
を定義します。
// implementsでtraitを適用する class P implements T {} // withTraitsでtraitを適用する class Q {}
P, Q に対してそれぞれメソッド b, c を呼び出します。メソッド b, c が期待通りに動くか検証してみます。
def p = new P() println p.b('hoge') println p.c(['hoge'])
def q = new Q().withTraits T println q.b('hoge') println q.c(['hoge'])
Groovy 2.3.6の仕様
実行結果は下記のようになりました。withTraits
で適用すると、trait 内のメソッドでクロージャを使うと、プライベートメソッドが見えないことが分かります。
--hoge-- [--hoge--]
--hoge-- groovy.lang.MissingMethodException: No signature of method: Q1_groovyProxy.a() is applicable for argument types: (java.lang.String) values: [hoge] Possible solutions: b(java.lang.Object), c(java.lang.Object), any(), is(java.lang.Object), any(groovy.lang.Closure), wait()
Groovy 2.4.0の仕様
実行結果は下記のようになりました。いずれの適用方法でも、trait 内のクロージャからプライベートメソッドが見えないことが分かります。見えない仕様に統一されたのかもしれません。
--hoge-- groovy.lang.MissingMethodException: No signature of method: P.a() is applicable for argument types: (java.lang.String) values: [hoge] Possible solutions: b(java.lang.Object), c(java.lang.Object), any(), is(java.lang.Object), any(groovy.lang.Closure), wait()
--hoge-- Caught: groovy.lang.MissingMethodException: No signature of method: Q1_groovyProxy.a() is applicable for argument types: (java.lang.String) values: [hoge] Possible solutions: b(java.lang.Object), c(java.lang.Object), any(), is(java.lang.Object), any(groovy.lang.Closure), wait()
詳しい情報をお持ちでしたらぜひ教えてください。traitで拡張可能なDSLを作ろうとするとこの仕様はつらいですね。
Gradle SSH Plugin 1.0.2をリリースした
Gradle SSH Plugin 1.0.2をリリースしました.
SSH Pluginを使うと,下記のようにGradleからSSHでコマンドを実行したりファイルを送受信したりできます.
// build.gradle plugins { id 'org.hidetake.ssh' version '1.0.2' } remotes { webServer { host = '192.168.1.101' user = 'jenkins' identity = file('id_rsa') } } task deploy << { ssh.run { session(remotes.webServer) { put 'example.war', '/webapps' execute 'sudo service tomcat restart' } } }
詳しい使い方は https://gradle-ssh-plugin.github.io をご覧ください.
New Feature in 1.0.2
put
コマンドでリモートのファイルに文字列を書き込めるようになりました.下記のようにファイルパス,文字列,バイト配列を指定できます.
// specify a file path, File object or Interable<File> put file: 'local_file', into: '/remote/file' put file: buildDir, into: '/remote/folder' put files: files('local_file1', 'local_file2'), into: '/remote/folder' // specify a string put text: 'hello world', into: '/remote/script.sh' // specify a byte array put bytes: [0xff, 0xff] as byte[], into: '/remote/fixture.dat'
下記のように複数行の文字列も指定できます.
put text: '''#!/bin/sh echo 'hello world' echo 'hoge' ''', into: '/tmp/deploy.sh'
実は,この機能は2011年頃のプロトタイプでは実装済みでしたが,プロダクトではずっと放置していました.