บทนำ

กลุ่มเป้าหมาย : นักพัฒนา Gradle ระดับกลางที่ต้องการจัดการการตั้งค่าที่ซับซ้อน.

ในการพัฒนาโปรเจ็กต์ปลั๊กอิน Gradle การจัดการการกำหนดค่าที่ซับซ้อนเป็นงานที่ทำบ่อย แทนที่จะทำให้ DSL เต็มไปด้วยหลายร้อยคุณสมบัติ มักจะดีและบำรุงรักษาง่ายขึ้นเมื่อกำหนดการตั้งค่าไว้ในไฟล์ภายนอก เช่น YAML บทความนี้แนะนำวิธีการผสานไลบรารีที่ทรงพลัง Jackson เพื่อแปลงไฟล์ YAML เป็นวัตถุ Kotlin โดยใช้แนวทาง TDD อย่างเข้มงวดเพื่อให้แน่ใจว่าปลั๊กอินของเรามีความแข็งแกร่ง`site-baker`.

บริบท : ปลั๊กอินของเรา `site-baker

ปลั๊กอินของเรา`site-baker`ถูกออกแบบมาเพื่อให้ทำการสร้างและเผยแพร่เว็บไซต์แบบคงที่ได้อย่างอัตโนมัติ. จำเป็นต้องอ่านไฟล์กำหนดค่า YAML (managed-jbake-context.yml) เพื่อรับข้อมูลต่าง ๆ เช่น เส้นทางของแหล่งที่มา จุดหมายในการปรับใช้ และรหัส Git

การกำหนดค่า YAML ที่เราต้องการทำการวิเคราะห์มีลักษณะดังนี้:

bake:
  srcPath: "./site/jbake"
  destDirPath: "bake"
  cname: "cheroliv.com"
pushPage:
  from: "bake"
  to: "cvs"
  repo:
    name: "trainings"
    repository: "https://github.com/pages-content/pages-content.github.io.git"
    credentials:
      username: "USERNAME"
      password: "SECRET_TOKEN"
  branch: "main"
  message: "cheroliv.com"
# ... autres configurations (pushMaquette, supabase, etc.)

การสร้างแบบจำลองการตั้งค่าใน Kotlin

ก่อนที่จะทำการพาร์ส เราต้องกำหนดโครงสร้างข้อมูลของเราในภาษา Kotlin ไว้ก่อน และแจ็คสันจะใช้คลาสเหล่านี้เพื่อแมปเนื้อหาของไฟล์ YAML ได้โดยอัตโนมัติ

// plugin/src/main/kotlin/com/cheroliv/site/baker/data/SiteConfiguration.kt
package com.cheroliv.site.baker.data

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule

fun parseSiteConfiguration(yaml: String): SiteConfiguration {
    val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
    return mapper.readValue(yaml)
}

data class GitPushConfiguration(
    val from: String = "",
    val to: String = "",
    val repo: RepositoryConfiguration = RepositoryConfiguration(),
    val branch: String = "",
    val message: String = "",
)

data class RepositoryConfiguration(
    val name: String = "",
    val repository: String = "",
    val credentials: RepositoryCredentials = RepositoryCredentials(),
) {
    companion object {
        const val ORIGIN = "origin"
        const val CNAME = "CNAME"
        const val REMOTE = "remote"
    }
}

data class RepositoryCredentials(val username: String = "", val password: String = "")

data class SiteConfiguration(
    val bake: BakeConfiguration = BakeConfiguration(),
    val pushPage: GitPushConfiguration = GitPushConfiguration(),
    val pushMaquette: GitPushConfiguration = GitPushConfiguration(),
    val pushSource: GitPushConfiguration? = null,
    val pushTemplate: GitPushConfiguration? = null,
    val supabase: SupabaseContactFormConfig? = null
)

data class BakeConfiguration(
    val srcPath: String = "",
    val destDirPath: String = "",
    val cname: String? = null,
)

// ... (autres data classes pour Supabase, si nécessaire)

ฟังก์ชัน`parseSiteConfiguration`เป็นจุดเริ่มต้นสำหรับการทำดีซีเรียลไลซ์ของเรา เธอใช้งาน`ObjectMapper`ของ Jackson, ที่กำหนดค่าโดยใช้`YAMLFactory`สำหรับรูปแบบ YAML และ`registerKotlinModule()`เพื่อการสนับสนุนคุณลักษณะเฉพาะของ Kotlin (เช่น ค่าเริ่มต้นของคุณสมบัติ).

3. การผสานรวม Jackson ใน `build.gradle.kts

เพื่อใช้งาน Jackson เราต้องเพิ่มการพึ่งพาที่จำเป็นลงใน`build.gradle.kts`ของโมดูลเรา`plugin`:

// plugin/build.gradle.kts
dependencies {
    // Jackson for YAML parsing
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.3")
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3")

    // ... autres dépendances
}

เราใช้`jackson-module-kotlin`เพื่อการสนับสนุน Kotlin และ`jackson-dataformat-yaml`เพื่อการจัดการรูปแบบ YAML

4. วิธีการ TDD : ทดสอบการวิเคราะห์ YAML

ตอนนี้เรามาใช้ TDD เพื่อตรวจสอบตรรกะการวิเคราะห์ของเรากันเถอะ

4.1. ทดสอบหน่วย : แมป YAML เป็นออบเจกต์ Kotlin

เราเริ่มต้นด้วยการทดสอบหน่วยใน`SiteBakerPluginTest.kt`เพื่อตรวจสอบว่าฟังก์ชั่น`parseSiteConfiguration`สามารถแปลงสตริง YAML เป็นวัตถุได้อย่างถูกต้อง`SiteConfiguration`.

// plugin/src/test/kotlin/com/cheroliv/site/baker/SiteBakerPluginTest.kt
@Test
fun `can map configuration text to SiteConfiguration object`() {
    val yamlString = "../../managed-jbake-context.yml"
        .run(::File)
        .readText()
        .trimIndent()

    val config: SiteConfiguration = parseSiteConfiguration(yamlString)

    assertEquals("cheroliv.com", config.bake.cname)
    assertEquals("main", config.pushPage.branch)
    assertEquals("https://github.com/pages-content/pages-content.github.io.git", config.pushPage.repo.repository)
}

การทดสอบนี้อ่านเนื้อหาของไฟล์`managed-jbake-context.yml`(ซึ่งจำเป็นต้องมีเพื่อให้การทดสอบเป็นที่ใช้ได้) และทำการยืนยันกับวัตถุนี้`SiteConfiguration`เป็นผลลัพธ์ที่ได้. ตรวจสอบว่าค่าคีย์ใน YAML นั้นถูกแมปอย่างถูกต้อง。

4.2. ทดสอบการทำงาน : ตรวจสอบการอ่านไฟล์การตั้งค่า

เพื่อให้แน่ใจว่าปลั๊กอินสามารถอ่านไฟล์การกำหนดค่าโดยใช้ DSL ของมันและการวิเคราะห์ทำงานในสภาพแวดล้อม Gradle จริง เราจึงเพิ่มการทดสอบฟังก์ชันลงใน`SiteBakerPluginFunctionalTest.kt`.

การทดสอบนี้มีความสำคัญเนื่องจากมันจำลองการดำเนินการของปลั๊กอินในโครงการจริง มันต้องเป็นปิดสนิท, กล่าวคือ เขาต้องสร้างไฟล์นี้ด้วยตัวเอง`managed-jbake-context.yml`ด้วยเนื้อหาที่ควบคุมไว้ เพื่อรับประกันความสามารถในการทำซ้ำและการแยกส่วนของการทดสอบ

// plugin/src/functionalTest/kotlin/com/cheroliv/site/baker/SiteBakerPluginFunctionalTest.kt
@Test
fun `config file contains good data`(){
    val configContent = configFile.readText(UTF_8)
    // Vérifications comme dans vos commentaires
    assertTrue(configContent.contains("bake"))
    assertTrue(configContent.contains("pushPage"))
    assertTrue(configContent.contains("repository"))
}

การทดสอบเชิงฟังก์ชันนี้ตรวจสอบว่า ไฟล์การกำหนดค่าที่ถูกคัดลอกไปยังโฟลเดอร์ชั่วคราวของการทดสอบ มีข้อมูลตามที่คาดหวัง. แม้ว่าการทดสอบนี้จะไม่ได้วิเคราะห์ YAML เป็นวัตถุ Kotlin โดยตรง แต่จะตรวจสอบความมีอยู่ของไฟล์และเนื้อหา ซึ่งเป็นขั้นตอนเบื้องต้นที่จำเป็นต่อการทำการวิเคราะห์โดยปลั๊กอิน

5. การแสดงผลของการไหลของการวิเคราะห์

แผนภาพต่อไปนี้แสดงให้เห็นการไหลของข้อมูลและปฏิสัมพันธ์ระหว่างการวิเคราะห์คอนฟิกูเรชัน YAML:

@startuml
actor "ผู้ใช้ Gradle" as User
participant "build.gradle.kts" as BuildScript
participant "SiteBakerPlugin" as Plugin
participant "SiteExtension" as Extension
participant "managed-jbake-context.yml" as ConfigFile
participant "parseSiteConfiguration()" as Parser
participant "SiteConfiguration (วัตถุ Kotlin)" as KotlinObject

User -> BuildScript : ใช้ปลั๊กอินและตั้งค่า DSL
BuildScript -> Plugin : apply()
Plugin -> Extension : สร้างและบันทึกส่วนขยาย 'site'
User -> BuildScript : ไซต์ { configPath = "..." }
BuildScript -> Extension : กำหนด configPath

Plugin -> ConfigFile : อ่านไฟล์ (ผ่าน configPath)
ConfigFile --> Plugin : เนื้อหา YAML
Plugin -> Parser : เรียก parseSiteConfiguration(yamlString)
Parser -> KotlinObject : แมพ YAML เป็นออบเจกต์ SiteConfiguration
KotlinObject --> Plugin : ส่งกลับวัตถุที่กำหนดค่าแล้ว

@enduml

สรุป

ด้วยการปฏิบัติตามแนวทาง TDD เราได้บูรณาการไลบรารี Jackson เพื่อทำการพาร์สไฟล์การกำหนดค่า YAML เป็นอ็อบเจ็กต์ Kotlin ในปลั๊กอิน Gradle ของเราอย่างสำเร็จ. วิธีนี้ทำให้เราสามารถ:

  • ทำการสร้างแบบจำลองอย่างชัดเจนการตั้งค่าของเราด้วยคลาส data ของ Kotlin

  • ตรวจสอบตรรกะของการพาร์ซด้วยการทดสอบหน่วยที่เฉพาะเจาะจง.

  • รับประกันการบูรณาการในสภาพแวดล้อม Gradle จริงๆ ผ่านการทดสอบเชิงฟังก์ชันที่เป็นเฮอร์เมติก

ฐานที่มั่นคงนี้ทำให้เรามีความมั่นใจอย่างยิ่งในการขยายปลั๊กอินของเราด้วยฟีเจอร์ต่างๆ ที่พึ่งพาการตั้งค่านี้ พร้อมทั้งรับประกันความสามารถในการบำรุงรักษาและความแข็งแกร่งของโค้ด การวิเคราะห์ YAML ได้กลายเป็นฟีเจอร์ที่เชื่อถือได้และได้รับการทดสอบแล้วของปลั๊กอินของเรา`site-baker`.

บทความที่เกี่ยวข้อง