Upgrade to Pro — share decks privately, control downloads, hide ads and more …

面倒なことはScalaスクリプトにやらせよう / let scala scripts do the troublesome things

面倒なことはScalaスクリプトにやらせよう / let scala scripts do the troublesome things

2019/09/13 Scala秋祭り

Takumi Kadowaki

September 16, 2019
Tweet

More Decks by Takumi Kadowaki

Other Decks in Programming

Transcript

  1. 面倒なことは
    Scalaスクリプトにやらせよう
    2019/09/16 Scala秋祭り
    Takumi Kadowaki @blac_k_ey

    View Slide

  2. どなた?
    Takumi Kadowaki
    Twitter: @blac_k_ey
    GitHub: NomadBlacky
    セプテーニ・オリジナル PYXISチーム所属
    広告運用自動化ツールのバックエンド開発が主な仕事
    海と温泉とテトリスが好き
    ScalaMatsuri2019で頒布した「技術読本」に
    「ServerlessFramework + Scala」なアプリケーションについて記事を書き
    ました

    View Slide

  3. 突然ですが問題です

    View Slide

  4. DockerHubからあるイメージのタグ一覧をAPIから取得して、
    最もサイズの小さいものと大きいものを出力してください
    {
    "count": 257,
    "page": 1,
    "page_size": 25,
    "results": [
    {
    "id": 2255500,
    "name": "stable",
    "last_updated": "2019-09-12T14:51:49.113501Z",
    "full_size": 50665062
    },
    {
    "id": 19451,
    "name": "latest",
    "last_updated": "2019-09-12T14:51:29.159817Z",
    "full_size": 50665667
    },
    ...
    ]
    }
    https://hub.docker.com/api/content/v1/repositories/public/library/nginx/tags

    View Slide

  5. シェルスクリプトで複雑なことをするとつらい…
    $ curl \
    https://hub.docker.com/api/.../nginx/tags \
    | jq .results[].full_size ... (ここで考えるのをやめる)
    (我々は jq コマンドのプロではない…)
    (ページングとか考えるとさらにめんどい…)
    とりあえずシェルスクリプトで…

    View Slide

  6. みんな大好きScala! みんな大好きsbt!

    View Slide

  7. 小さいプログラムを書くにはオーバー…
    ● build.sbt
    ○ ビルド定義
    ○ 依存解決
    ● build.properties
    ○ sbtバージョンの指定
    ○ プラグイン
    (sbt-assembly etc.)
    ● src/main/scala/.../Hoge.scala
    ○ 目的のコード

    View Slide

  8. 世の中には色々なスクリプト言語がある
    JavaScript
    Ruby
    Python
    Groovy
    PHP Perl
    etc...

    View Slide

  9. それでもやっぱりScalaで書きたい〜
    JavaScript
    Ruby
    Python
    Groovy
    PHP Perl

    View Slide

  10. この間を埋めるものはないだろうか…
    コード量/プロジェクト規模


    Shell Script

    View Slide

  11. そんなあなたに、
    Ammonite


    @
    コード量/プロジェクト規模
    Shell Script

    View Slide

  12. Ammoniteとは? https://ammonite.io
    lihaoyi/Ammonite
    ● Scala REPL
    ● Script Runner
    ● File System Library
    ● Shell
    これらの総称

    View Slide

  13. Ammonite-REPL

    View Slide

  14. http://ammonite.io/#Ammonite-REPL
    何はともあれ、まずはインストールしてみよう
    (Linuxなど ※執筆時の最新版)
    $ sudo sh -c '(echo "#!/usr/bin/env sh" && curl -L
    https://github.com/lihaoyi/Ammonite/releases/download/1.6.9/2.13-1.6.9) >
    /usr/local/bin/amm && chmod +x /usr/local/bin/amm' && amm
    コマンドをひとつ実行するだけ!
    (Macのかた)
    $ brew install ammonite-repl

    View Slide

  15. Hello World!!

    View Slide

  16. Ammonite-REPLの便利な機能
    ● シンタックスハイライト
    ● 複数行のコード編集
    ● Undo & Redo
    ● Magic Import
    ● browse
    ○ 大きな配列など、文字列表現が長い評価結果をページャで確認
    ● source
    ○ REPL上でソースコードを閲覧

    View Slide

  17. Magic Import
    Maven Central からJarを取得して、すぐにライブラリを使える!
    import $ivy.`:::`
    // cats の例
    import $ivy.`org.typelevel::cats-core:2.0.0`
    // sbt との比較
    libraryDependencies +=
    "org.typelevel" %% "cats-core" % "2.0.0"

    View Slide

  18. View Slide

  19. Ammonite-Script

    View Slide

  20. Scalaコードをコマンドラインから直接コンパイル・実行!
    sample.sc
    println("Hello Ammonite!!")

    View Slide

  21. Scalaコードをコマンドラインから直接コンパイル・実行!
    sample.sc
    println("Hello Ammonite!!")
    Scalaスクリプトでよく使われる拡張子

    View Slide

  22. Scalaコードをコマンドラインから直接コンパイル・実行!
    sample.sc
    println("Hello Ammonite!!")
    $ amm sample.sc
    amm コマンドの後ろにスクリプトのパ

    View Slide

  23. Scalaコードをコマンドラインから直接コンパイル・実行!
    sample.sc
    println("Hello Ammonite!!")
    $ amm sample.sc
    Compiling (synthetic)/ammonite/predef/DefaultPredef.sc
    Compiling /home/blacky/sample.sc
    Hello Ammonite!!
    その場でコンパイル &実行!

    View Slide

  24. Scalaコードをコマンドラインから直接コンパイル・実行!
    sample.sc
    println("Hello Ammonite!!")
    $ amm sample.sc
    Compiling (synthetic)/ammonite/predef/DefaultPredef.sc
    Compiling /home/blacky/sample.sc
    Hello Ammonite!!
    $ amm sample.sc
    Hello Ammonite!!
    2回目以降はコンパイルしないので実行が早い !

    View Slide

  25. Magic Importとの合わせ技
    sample_magic.sc
    import $ivy.`org.jsoup:jsoup:1.12.1`
    val doc =
    org.jsoup.Jsoup.connect("https://www.google.com").get
    println(doc.getElementsByTag("title"))
    $ amm sample_magic.sc
    Google
    もちろんスクリプトでも Magic Importが使える!

    View Slide

  26. Ammoniteに含まれるライブラリ依存

    View Slide

  27. デフォルトで使える便利なライブラリたち
    Ammoniteに含まれている依存はMagicImportをせずとも使えます。
    その中でもスクリプトの実装に役立つライブラリを一部紹介します。
    $ coursier resolve com.lihaoyi:ammonite_2.13.0:1.6.9
    ...
    com.lihaoyi:ammonite-ops_2.13:1.6.9:default
    com.lihaoyi:os-lib_2.13:0.3.0:default
    com.lihaoyi:requests_2.13:0.2.0:default
    com.lihaoyi:upickle_2.13:0.7.5:default
    ...
    ...

    View Slide

  28. デフォルトで使える便利なライブラリたち
    ● ammonite-ops / os-lib
    ○ https://github.com/lihaoyi/os-lib
    ○ ファイル操作、サブプロセス実行などを提供
    ● requests
    ○ https://github.com/lihaoyi/requests-scala
    ○ HTTPクライアント
    ● upickle
    ○ https://github.com/lihaoyi/upickle
    ○ シリアライゼーションライブラリ
    ○ MsgPack / JSON に対応

    View Slide

  29. 実際の活用例

    View Slide

  30. esa.ioの利用状況をDatadogで可視化してみる
    Stats取得 メトリクス送信
    GitLab CI の Scheduled Jobで
    1時間に1回実行

    View Slide

  31. ソースコード
    スライドには収まりきらなかったのでgistで…
    https://gist.github.com/NomadBlacky/2c56814a1993e327ed2c2e3d
    8da01eed

    View Slide

  32. GitLab CI での実行ログ
    ./amm esa-stats-to-datadog.sc --team septeni-original
    Get septeni-original.esa.io stats ...
    {
    "members": 55,
    "posts": 298,
    "posts_wip": 51,
    "posts_shipped": 247,
    "comments": 157,
    "stars": 197,
    "daily_active_users": 1,
    "weekly_active_users": 13,
    "monthly_active_users": 26
    }
    Post stats to Datadog ...
    Done!

    View Slide

  33. あとはダッシュボードを作ってあげるだけ

    View Slide

  34. Scala製のDatadog APIクライアントを作りました! Starください!!!
    https://github.com/NomadBlacky/scaladog
    (宣伝)

    View Slide

  35. そのほか…
    ● Twitterの「いいね」から画像を取得する
    ○ https://github.com/NomadBlacky/ammonite-scripts/blob/master/bin/get-twitter-images.sc
    ● 何度閉じてもダイアログが出てくるScalaスクリプト
    ○ https://gist.github.com/NomadBlacky/a70d76f5d699994da83c92775935f3f6
    ○ 実行は自己責任でお願いします
    ● ScalaからAWS CDKを叩いてインフラを構築する
    ○ http://nomadblacky.hatenablog.com/entry/2019/07/14/194607
    ○ Scalaでインフラを作る!

    View Slide

  36. おわりに

    View Slide

  37. 参考資料
    ● Document
    ○ http://ammonite.io/
    ● GitHub
    ○ https://github.com/lihaoyi/ammonite
    ● Blog
    ○ http://www.lihaoyi.com
    ● Video
    ○ https://vimeo.com/148552858
    ● IntelliJ IDEA + AmmoniteでScalaスクリプトの開発を始めよう
    ○ 弊社エンジニアブログ
    ○ http://labs.septeni.co.jp/entry/2018/12/20/120000

    View Slide

  38. まとめ
    ● 便利なREPLとしてのAmmonite
    ○ ちょっとScalaを書いて試したい時にAmmoniteを!
    ○ 特に「Magic Import」が便利!
    ● ScalaスクリプトとしてのAmmonite
    ○ シェルスクリプトだと面倒、sbtだとオーバーという時の選択肢に!
    ○ ざっくり書いてもコンパイラが怒ってくれる
    ■ (ちょっとしたテスト代わり)
    ○ Java/Scalaの資産が使える!
    ○ 何よりScalaでスクリプトを書ける!!
    ■ 楽しい!!!!

    View Slide

  39. ちなみに: 最初の問題の回答
    import upickle.default._
    case class Response(page: Int, page_size: Int, results: Seq[Tag])
    case class Tag(name: String, full_size: Long)
    implicit val responseReader: Reader[Response] = macroR
    implicit val tagReader: Reader[Tag] = macroR
    def fetchAllTags(image: String): Seq[Tag] =
    Stream.from(1).map { page =>
    read[Response](
    requests.get(
    url =
    s"https://hub.docker.com/api/content/v1/repositories/public/library/$image/tags",
    params = Map("page" -> page.toString)
    ).text
    )
    }.takeWhile(r => r.page < r.page_size)
    .flatMap(_.results)
    @main def main(image: String): Unit = {
    val allTags = fetchAllTags(image)
    println(allTags.minBy(_.full_size))
    println(allTags.maxBy(_.full_size))
    }

    View Slide