「つくってあそぼ Kotlin DSL ~拡張編~」を発表しました
はじめに
【11/2(金) 大阪】第2弾クラスメソッドのモバイル開発を知る!第2回〜Android編で「つくってあそぼ Kotlin DSL ~拡張編~」を発表しました。
モバイルメソッド大阪 第3回 #mobilemethodでは、DSLの作り方を発表し、今回はDSLの利用の仕方にフォーカスしました。
発表資料
ソースコード
object Validations { inline fun <reified T> define(init: ValidationBuilder<T>.() -> Unit) { val validation = ValidationBuilder<T>().apply(init).build() val key = (T::class.java).run { "${`package`.name}.$simpleName" } map[key] = validation } inline fun <reified T> key(): String { val key = (T::class.java).run { "${`package`.name}.$simpleName" } return key } val map = mutableMapOf<String, Validation<*>>() operator fun invoke(init: Validations.() -> Unit) { init() } fun get(key: String): Any? { return map[key] } inline fun <reified T> get(): Validation<T> { val key = (T::class.java).run { "${`package`.name}.$simpleName" } val validation = map.getOrElse(key) { throw IllegalArgumentException("not found validation: $key") } return (validation as Validation<T>) } inline fun <reified T> validate(entity: T): Map<String, List<String>> { val validation = get<T>() return validation.validate(entity) } } interface Validable<T> { val key: String fun validate(): Map<String, List<String>> = (Validations.get(key) as Validation<T>).validate(this as T) } inline fun <reified T> Validable<T>.key() = Validations.key<T>() object ValidableDelegate { inline fun <reified T> get() = Validable<T>(Validations.key<T>()) class Validable<T>(private val key: String) : ReadOnlyProperty<Any, Validation<T>> { override fun getValue(thisRef: Any, property: KProperty<*>): Validation<T> { return Validations.get(key) as Validation<T> } } } class Validation<T>(val validations: Map<String, ChildValidation<T>>) { companion object { inline operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> { val builder = ValidationBuilder<T>().apply(init) return builder.build() } } fun validate(value: T): Map<String, List<String>> { val messages = mutableMapOf<String, List<String>>() validations.forEach { map -> val errors = map.value.validations .asSequence() .filter { !it.first.invoke(value) } .map { it.second } .filter { it.isNotEmpty() } .toList() if (errors.isNotEmpty()) { messages.put(map.key, errors) } } return messages } } class ValidationBuilder<T> { val validations = mutableMapOf<String, ChildValidation<T>>() operator fun String.invoke(init: ChildValidation<T>.() -> Unit) { validations.put(this, ChildValidation<T>().apply(init)) } fun build(): Validation<T> { return Validation(validations) } } class ChildValidation<T> { val validations = mutableListOf<Pair<T.() -> Boolean, String>>() fun be(f: T.() -> Boolean) = f class ErrorMessageSyntax<T>(val validation: T.() -> Boolean) class WithCallbackSyntax<T>(val validation: T.() -> Boolean) infix fun (T.() -> Boolean).not(syntax: error) = ErrorMessageSyntax(this) infix fun (T.() -> Boolean).not(syntax: with) = WithCallbackSyntax(this) infix fun WithCallbackSyntax<T>.callback(pair: Pair<() -> Unit, String>) { } operator fun String.invoke(f: () -> Unit): Pair<() -> Unit, String> { return f to this } infix fun ErrorMessageSyntax<T>.message(message: String) { validations.add(Pair(this.validation, message)) } infix fun (T.() -> Boolean).not(message: String) { validations.add(Pair(this, message)) } } object error object with
あとがき
前回に引き続き前日にボードゲーム会も開催し、満喫した大阪でした。
第2回 OsakaBoardGameを開催します✨
日時: 11/1(木) 19:00〜
場所: 株式会社クラスメソッドの大阪オフィス(7F初めましての方も大歓迎です。詳細はリンク先をご確認ください。
皆さまのご参加、お待ちしております(*´꒳`*)#atnd #akibabg https://t.co/fv8f88SWZe #ボドゲ会 #クラメソボドゲ部— クラスメソッド ボードゲーム部 (@CMBoardGameClub) 2018年10月17日
これでKotlin DSL系の話は一段落つきました。、Kotlin 1.3の新機能を楽しみしたいと思います。