一角獣は夜に啼く

ただの日記です。

思ってることとか考えたこととか適当に書きます。 主にソフトウェア開発の話題を扱う 「ひだまりソケットは壊れない」 というブログもやってます。

Kotlin に関して最近取り組んでいるもの

「Kotlin Fest 2018」 がいよいよ明日になりましたね! 私も何か LT しようというつもりだったんですが、チケット販売開始直後に LT 枠のチケットを買おうとしてカード決済周りでまごついてたらチケットが売り切れちゃってました! みんな熱い!!!

というわけで (?)、Kotlin を使った最近の個人的な取り組みを書き残しておきます。 「Kotlin Fest 2018」 に参加する人で何か興味があるものがあれば、ぜひ会場でお話ししましょう!

最近 Kotlin で取り組んでいるもの

DB アクセスライブラリを書いてる

個人的に Ktor で web アプリケーションを書こうとしているのですが、DB アクセスライブラリとして何を使うのかで悩んで、ExposedJdbi 3 を試してなかなかしっくりこず、最終的に自分で書いてみてます。

API はリレーショナルデータベースの思想にのっとるつもりで、インターフェイスに拡張関数として定義します。 んで、実際の DB アクセスの処理は拡張関数を持つインターフェイスの実装をリフレクションで提供する、という感じです。

// リレーションの各属性を持つタプルを表すクラスを定義。
data class FooTuple(
    @AttributeName("id") val id: Long,
    @AttributeName("value") val value: String
)

// リレーションを表すインターフェイスを定義。
@RelationName("foo")
interface FooRelation : BareRelation<FooTuple>

// リレーションの実態とリレーションに対する操作を定義。
interface FooRelationContext {
    val fooRelation: FooRelation

    @Insert
    fun FooRelation.insert(value: FooTuple)
}

// 複数リレーションを扱うコンテキストを複数集めてアプリケーション用の
// DB アクセスのコンテキストを定義。
// OrmQueryContext は、各種リレーションに対する select 操作を提供するインターフェイス。
interface AppOrmContext : OrmQueryContext, FooRelationContext

// 以下のような感じでインターフェイスの実装を生成できる。
val connection: Connection
val ormContext = JdbcOrmContexts.create(AppOrmContext::class, connection)

// 下記のような感じで insert したり select したりできる。
with (ormContext) {
    fooRelation.insert(FooTuple(1, "Hello"))
    val selected = fooRelation.select(where { FooTuple::id eq 1 }).toSet()
}

今のところは JDBC を使った DB アクセスの実装を軽く書いてるというぐらいなのですが、オンメモリでリレーションの実装を提供する仕組みも作るつもりです。 (そうするといちいちモックを定義しなくても DB アクセスを含むユニットテストが書けるようになって便利。 戦術的 DDD のリポジトリインターフェイスにしてテスト時にはオンメモリの実装を提供するなどすることでテストしやすくするのと同様のことをもう少し低レイヤで実現したい。)

内部的にはリフレクションでごりごりやっているので、Kotlin のリフレクション周りを触る人と知見交換したいなーという気持ちです!

Ktor での Twitter ログイン

Ktor の routing の中で以下のような感じでメソッドを呼ぶことで Twitter ログインのエンドポイントをはやすことができる仕組みも書きました。

        setupTwitterLogin(
            "/auth/twitter/start", "/auth/twitter/callback",
            "http://localhost:8080", twitterClientCredentials, env,
            object : OutputPort {
                override val success: OutputInterceptor<TwitterToken> = { token ->
                    // Twitter ログイン成功時の処理。
                }
                override val twitterCallFailed: OutputInterceptor<TwitterCallFailedException> = {
                    // Twitter との通信に失敗した時の処理。
                }
                override val temporaryCredentialNotFound: OutputInterceptor<TemporaryCredentialNotFoundException> = {
                    // Temporary Credential が見つからなかった時の処理。
                }
            })

こっちは普通に実装しただけですが、Ktor で Twitter ログインを実装したい人の参考になれば。 (そのうちライブラリ化するつもりではある。)

WebDriver 経由でブラウザ上で処理を実行してスクリーンショットを返す、という Ktor アプリケーションを書いてる

Ktor から WebDriver を使ってリモートエンドのブラウザ上で何か処理をして、そのスクリーンショットを Ktor のレスポンスとして返す、というような web アプリケーションを書いています。

Ktor へのリクエストをキューにためて WebDriver を使ってリモートエンドのブラウザ上で処理を行う、みたいな流れや、WebDriver のセッション管理、ヘルスチェックのための WebDriver へのリクエストをいい感じに割り込ませる、みたいな仕組みのためにコルーチンをいい感じ (?) に使ってます。 「Kotlin Fest 2018」 では八木さんのコルーチンについてのセッション 「Kotlin コルーチンを理解しよう」 もあるので楽しみです。

Kotlin を愛でていきましょう!

Kotlin がんがん使っていくぞ!!!