مقدمة

الجمهور المستهدف: مطورو Gradle متوسطو المستوى الذين يرغبون في إدارة تكوينات معقدة.

في تطوير إضافات Gradle، إدارة التكوينات المعقدة هي مهمة شائعة. بدلاً من تحميل DSL بمئات الخصائص، غالبًا ما يكون من الأنظف والأكثر قابلية للصيانة تحديد التكوين في ملف خارجي، مثل YAML. يرشدك هذا المقال خلال تكامل المكتبة القوية Jackson لتحليل ملفات YAML إلى كائنات Kotlin، مع اتباع نهج TDD صارم لضمان متانة برنامجنا الإضافي`site-baker`.

1. السياق: المكوّن الإضافي الخاص بنا `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.)

2. نمذجة التكوين في كوتولين

قبل التحليل، يجب علينا تحديد هيكل بياناتنا بلغة Kotlin. سيستخدم Jackson هذه الفئات لتحويل محتوى 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`من جاكسون، مُهيأ مع`YAMLFactory`لتنسيق YAML و`registerKotlinModule()`لدعم ميزات Kotlin (مثل القيم الافتراضية للخصائص).

3. تكامل جاكسون في `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 الخاصة بنا. هذه الطريقة أتاحت لنا:

  • نمذجة بوضوحتكويننا مع فئات البيانات Kotlin.

  • التحقق من منطق التحليلمع اختبارات وحدة مستهدفة.

  • ضمان التكاملفي بيئة Gradle الحقيقية بفضل اختبارات وظيفية محكمة.

تمنحنا هذه القاعدة الصلبة ثقة كبيرة لتوسيع مكوننا الإضافي بميزات تعتمد على هذه الإعدادات، مع ضمان صيانة الكود ومتانته. أصبح تحليل YAML ميزة موثوقة ومختبرة في مكوننا الإضافي`site-baker`.

مقالات ذات صلة