Embulkのpluginを書いてみようかなと思ったんですが、せっかくならScalaで書いてみようかなと思ってその時の備忘録です。
2. 準備
2.1. pluginの作成
色々ためしたレポジトリはこんな感じです。
とりあえずJavaでpluginを書く準備をします。
利用したEmbulkのversionは 0.7.4です
#embulk コマンドはインストール済み前提です。 $embulk --version embulk 0.7.4 $embulk new java-input myappwithscala ... $cd embulk-input-myappwithscala $tree -L 3 . ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib │ └── embulk │ └── input └── src ├── main │ └── java └── test └── java
2.2. Scalaで書く準備
2つ変更があります。
2.2.1. build.gradleにScala用の設定を追記
- pluginsに id"scala" を追記
- dependenciesに compile "org.scala-lang:scala-library:2.11.6" , testCompile 'org.scalatest:scalatest_2.11:2.2.4', testRuntime 'org.scala-lang.modules:scala-xml_2.11:1.0.3' を追記
- 新しくtasks.withType(ScalaCompile) { scalaCompileOptions.useAnt = false } を追加
上記以外は初期のままです。
plugins { id "com.jfrog.bintray" version "1.1" id "com.github.jruby-gradle.base" version "0.1.5" id "scala" // scalaを追加 + javaは使わないつもりだったので削除した } import com.github.jrubygradle.JRubyExec repositories { mavenCentral() jcenter() } configurations { provided } version = "0.1.0" dependencies { compile "org.scala-lang:scala-library:2.11.6" // scala用に追加 compile "org.embulk:embulk-core:0.7.4" provided "org.embulk:embulk-core:0.7.4" // compile "YOUR_JAR_DEPENDENCY_GROUP:YOUR_JAR_DEPENDENCY_MODULE:YOUR_JAR_DEPENDENCY_VERSION" testCompile "junit:junit:4.+" testCompile 'org.scalatest:scalatest_2.11:2.2.4' // 同じくscalaように追加 testRuntime 'org.scala-lang.modules:scala-xml_2.11:1.0.3' // 同じように(ry } // compile時に差分コンパイルにしてもらうための設定とかなんとか // Activating the Zinc based compiler tasks.withType(ScalaCompile) { scalaCompileOptions.useAnt = false } task classpath(type: Copy, dependsOn: ["jar"]) { doFirst { file("classpath").deleteDir() } from (configurations.runtime - configurations.provided + files(jar.archivePath)) into "classpath" } clean { delete "classpath" } task gem(type: JRubyExec, dependsOn: ["gemspec", "classpath"]) { jrubyArgs "-rrubygems/gem_runner", "-eGem::GemRunner.new.run(ARGV)", "build" script "${project.name}.gemspec" doLast { ant.move(file: "${project.name}-${project.version}.gem", todir: "pkg") } } task gemPush(type: JRubyExec, dependsOn: ["gem"]) { jrubyArgs "-rrubygems/gem_runner", "-eGem::GemRunner.new.run(ARGV)", "push" script "pkg/${project.name}-${project.version}.gem" } task "package"(dependsOn: ["gemspec", "classpath"]) << { println "> Build succeeded." println "> You can run embulk with '-L ${file(".").absolutePath}' argument." } task gemspec { ext.gemspecFile = file("${project.name}.gemspec") inputs.file "build.gradle" outputs.file gemspecFile doLast { gemspecFile.write($/ Gem::Specification.new do |spec| spec.name = "${project.name}" spec.version = "${project.version}" spec.authors = ["Arata Tanaka"] spec.summary = %[Myappwithscala input plugin for Embulk] spec.description = %[Loads records from Myappwithscala.] spec.email = ["tarata43@yahoo.co.jp"] spec.licenses = ["MIT"] # TODO set this: spec.homepage = "https://github.com/tarata43/embulk-input-myappwithscala" spec.files = `git ls-files`.split("\n") + Dir["classpath/*.jar"] spec.test_files = spec.files.grep(%r"^(test|spec)/") spec.require_paths = ["lib"] #spec.add_dependency 'YOUR_GEM_DEPENDENCY', ['~> YOUR_GEM_DEPENDENCY_VERSION'] spec.add_development_dependency 'bundler', ['~> 1.0'] spec.add_development_dependency 'rake', ['>= 10.0'] end /$) } } clean { delete "${project.name}.gemspec" }
2.2.2. PluginのクラスをScalaに変更
そのままScala用に変えました。
Tips
- JavaのintはScalaのInt型に
- Listはそのままjava.util.Listに
- アホな事に拡張子が.scalaになっていないとgradlwでコンパイルしてくれないことにすごいハマったので注意です(´・ω・`)
package org.embulk.input.myappwithscala import com.google.common.base.Optional import org.embulk.config._ import org.embulk.spi._ class MyappwithscalaInputPlugin extends InputPlugin { trait PluginTask extends Task { // configuration option 1 (required integer) @Config("option1") def getOption1:Int // configuration option 2 (optional string, null is not allowed) @Config("optoin2") @ConfigDefault("\"myvalue\"") def getOption2:String // configuration option 3 (optional string, null is allowed) @Config("optoin3") @ConfigDefault("null") def getOption3:Optional[String] // if you get schema from config @Config("columns") def getColumns:SchemaConfig } override def transaction(config:ConfigSource, control:InputPlugin.Control):ConfigDiff = { val task:PluginTask = config.loadConfig(classOf[PluginTask]) val schema = task.getColumns.toSchema val taskCount = 1 // number of run() method calls resume(task.dump(), schema, taskCount, control) } override def resume( taskSource:TaskSource, schema:Schema, taskCount:Int, control: InputPlugin.Control ): ConfigDiff = { control.run(taskSource, schema, taskCount) Exec.newConfigDiff() } override def cleanup( taskSource:TaskSource, schema:Schema, taskCount:Int, successTaskReports:java.util.List[TaskReport]): Unit = () @Override override def run( taskSource:TaskSource, schema:Schema, taskIndex:Int, output:PageOutput ):TaskReport = { val task:PluginTask = taskSource.loadTask(classOf[PluginTask]) // Write your code here :) println(schema.toString) // Exec.newCommitReport() throw new UnsupportedOperationException("MyappwithscalaInputPlugin.run method is not implemented yet") } override def guess(config:ConfigSource):ConfigDiff = { Exec.newConfigDiff() } }
3. なんとなく動かしてみる
このまま動かしても throw new UnsupportedOperationException("MyappwithscalaInputPlugin.run method is not implemented yet") が記述されてるので例外が出ますが、今回具体的な実装はしていないので、その例外が発生することがゴールということにしようと思っています。
configファイルは下記のような感じです
in: type: myappwithscala option1: 1 option2: example2 columns: [] out: type: stdout
この状態でテスト的に動かしてみようと思います。
$./gradlew classpath :compileJava UP-TO-DATE :compileScala UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :jar UP-TO-DATE :classpath UP-TO-DATE BUILD SUCCESSFUL Total time: 17.26 secs This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.6/userguide/gradle_daemon.html $embulk run -I lib ./example.yml 2015-09-21 21:58:08.735 +0900: Embulk v0.7.4 2015-09-21 21:58:13.352 +0900 [INFO] (transaction): Loaded plugin embulk/input/myappwithscala from a load path 2015-09-21 21:58:13.511 +0900 [INFO] (transaction): {done: 0 / 1, running: 0} Schema{ } 2015-09-21 21:58:13.774 +0900 [INFO] (transaction): {done: 1 / 1, running: 0} org.embulk.exec.PartialExecutionException: java.lang.UnsupportedOperationException: MyappwithscalaInputPlugin.run method is not implemented yet # 中略 Error: java.lang.UnsupportedOperationException: MyappwithscalaInputPlugin.run method is not implemented yet
エラーが出たようです。
消し忘れてたprintlnも表示されてますね
こんな感じでScalaで一応書けそうです。
そのうちplugin公開してみようかなと思います。