この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
【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の新機能を楽しみしたいと思います。