秋アニメは何観てますか?
こんなグラフを見たことある方はいないでしょうか?
実はこれ、友達が作成して公開しているものだったようです(知ったときは驚きました)。 グラフ作成のためのデータを取得するAPIも公開していて、これが今scalaベースで動いています。
どうも最近彼はこの個人プロジェクトに情熱を傾けているので、色々話を聞いていたら、いつの間にか自分も参加していました(ぇ
というわけで、今回はSora APIを叩くScalaのサンプルコードについてです。
ちなみに私は「すべてがFになる」を観ています
原作を読んだのは大学生の頃で、その頃はこれがアニメになるとも、将来それを自分が観てるとも思わなかったですが、相変わらずコードは書いているので、不思議な連続感を感じています。
書いてるコードはCからScalaに変わっていましたが。
sbt設定の手直し
さて、さっそくScalaのコードをEclipseで書こうと思ったのですが、sbtがエラーを吐くのでその修正から行うことになりました。
しかし、sbt使おうとするといつも不具合対応しているような気がします。利用する機会が不定期なのが悪いんですかね。
ちなみに実行環境は以下の通りです。
- Windows 8.1
- JDK 1.8.0_60
- Scala 2.11.7
- sbt 0.13.9
ユーザホーム\.sbt\repositoriesのfileプロトコル指定が間違っている
sbt
コマンドを実行すると、以下のようなエラーになりました。
$ sbt java.lang.IllegalArgumentException: URI has an authority component at java.io.File.<init>(File.java:423) at sbt.Classpaths$.sbt$Classpaths$$bootRepository(Defaults.scala:1758) at sbt.Classpaths$$anonfun$appRepositories$1.apply(Defaults.scala:1729) at sbt.Classpaths$$anonfun$appRepositories$1.apply(Defaults.scala:1729) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33) at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:34) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at sbt.Classpaths$.appRepositories(Defaults.scala:1729) at sbt.Classpaths$$anonfun$41.apply(Defaults.scala:1102) at sbt.Classpaths$$anonfun$41.apply(Defaults.scala:1102) at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47) at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47) at sbt.EvaluateSettings$MixedNode.evaluate0(INode.scala:175) at sbt.EvaluateSettings$INode.evaluate(INode.scala:135) at sbt.EvaluateSettings$$anonfun$sbt$EvaluateSettings$$submitEvaluate$1.apply$mcV$sp(INode.scala:69) at sbt.EvaluateSettings.sbt$EvaluateSettings$$run0(INode.scala:78) at sbt.EvaluateSettings$$anon$3.run(INode.scala:74) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) [error] java.lang.IllegalArgumentException: URI has an authority component [error] Use 'last' for the full log. Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore?
以下に書いてありましたが、repositoriesファイルの中の記述にダメなところがあるようです。
ファイル指定のスラッシュが足りないようなので、file://をfile:///に修正したらエラーは消えました。
修正対象ファイル
C:\Users\ユーザ名.sbt\repositories
Permanent指定のオプションを無効化
これは別に不具合ではないのですが、Java 8からPermanent領域が消えているので、 sbtconfig.txtからMaxPermSizeオプションを外します(付けているとWarningが出るので)。
- C:\Program Files (x86)\sbt\conf\sbtconfig.txt
変更前:-XX:MaxPermSize=256m 変更後:# -XX:MaxPermSize=256m
オプションについての詳しい話は、以下が参考になります。
Eclipseプラグインの有効化
前は普通に実行できていた気がするのですが、
sbt eclipse
コマンドもエラーが出たので、公式の記述の通りに対応しました。
- 対処法
C:\Users\ユーザ名.sbt\0.13\pluginsにplugins.sbtファイルを作成して、以下を書き込み
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
その後、Eclipseプロジェクトを作成するフォルダ配下でsbt eclipse
を実行。
- 参考
github.com
sbt、まとめました
ここら辺のことはQiitaにまとめました。 新規インストール時の参考にどうぞ。
Scalaプログラミングをするぞ!
これでScalaを書く準備ができました。
私のScalaの先生は、コップ本(第1版)とScala逆引きレシピなのですが、Scalaのバージョンアップの影響をどちらも受けていて、 書いている通りにやろうとすると結構な率でハマります。そして言うまでもなく、今回もしっかりハマりました。
逆引きレシピの改訂版が待ち遠しいです。
Sora APIの利用サンプル(Scala)
以下がアニメ情報を取得するSora APIを、Scalaを使ってGETで叩き、JSONデータを取得するサンプルコードです。
APIのサンプルというよりは、Play JSON libraryとDispatchのサンプルみたいになってしまいました。
Dispatchは予めハマっていたので今回はそれほどハマらなかったのですが、 Play JSONのJSONからScala変換で結構ハマりました。
以下がそのJSON to Scalaを実行している箇所のコードを抜き出したものになるのですが、
- JsValueはListとして保持されているので、一度Listから取り出して処理をしないといけない。
- JSONのvalue値を文字列比較で一致を見るときは、そのまま文字列で比較するのではなく、JsStringのオブジェクトを使って比較すること。
- JSONのvalue値をStringに変換しても、ダブルクォートが除去されず、文字列としてくっついたまま返却されてくること。
これらのことに気をつけて、やっと得たかった情報を得ることができました。
case Success(content) => { val responseJson = Json.parse(content) println("GET /anime/v1/master/2015/2 json response" + responseJson.toString()) val animeInfo = responseJson.as[List[JsValue]] map { seed => seed.as[Map[String, JsValue]] } // 取得した情報から、「てさぐれ!部活もの すぴんおふ プルプルんシャルムと遊ぼう」の情報を利用する場合 val tesagure: List[Map[String, JsValue]] = for ( anime <- animeInfo if anime("title_short1") == JsString("てさぐれ!部活もの") ) yield anime println("\n「てさぐれ!部活もの すぴんおふ プルプルんシャルムと遊ぼう」 のアニメ情報") tesagure.head.toSeq.sortBy(_._1).foreach { parameter => // valueをString変換してもダブルクォートが付いたままなので、表示前に除去する val value = parameter._2.toString().replaceFirst("""^\"(.*)\"$""", "$1") println(s""" ${parameter._1} : ${value}""") } }
また、これらの処理はonComplete式中で実行しているのですが、そうなると非同期処理として実施されるため、 このままだとEclipseで実行したプロセスが終了せずに残り続ける問題がありました。
そこで今回は簡単な対処として、2秒待ったらプログラム終了、というコードを最後に入れています。
// 非同期処理の終了 Thread.sleep(2000) sys.exit(0)
最後のsys.exit(0)
はsys.exit()
でも同じ挙動のはずです。
しかし、これはもうちょっとスマートに対応したいものですね。いい方法が見つかったら更新しておきます。 他にいい方法があるよ!という知見を持っている方がいればプルリク等で指摘いただけると助かります。