Skip to content

Viper

عنوان المستودع: spf13/viper: Go configuration with fangs (github.com)

عنوان الوثائق: spf13/viper: Go configuration with fangs (github.com)

TIP

المسؤولون يناقشون الانتقال إلى Viper2، يمكن للمهتمين التعرف على: Viper2

التثبيت

go get github.com/spf13/viper

مقدمة

Viper، هو حل كامل لملفات التكوين لتطبيقات Go، يمكنه التعامل مع جميع أنواع متطلبات وصيغ التكوين تقريبًا، ويسهل إدارة ملفات تكوين المشروع، ويتميز بالميزات التالية:

  • إعداد القيم الافتراضية
  • دعم الصيغ JSON, TOML, YAML, HCL, envfile، Java properties
  • دعم المراقبة في الوقت الفعلي وإعادة تحميل ملفات التكوين
  • دعم القراءة من متغيرات البيئة
  • دعم القراءة من أنظمة التكوين عن بُعد ومراقبة التغييرات
  • دعم قراءة علامات سطر الأوامر
  • دعم القراءة من المخزن المؤقت
  • دعم إعداد القيم بشكل صريح

المسؤولون يقولون إن Viper يمكنه تلبية جميع متطلبات تكوين التطبيقات، يحتاج المطورون فقط للتركيز على بناء التطبيق، والسماح لـ Viper بإدارة التكوين، والعديد من المشاريع الشهيرة تستخدم Viper

DANGER

Viper ليس مسؤولًا عن تشفير وفك تشفير ملفات التكوين، أي أنه لن يقوم بأي معالجة أمنية لملفات التكوين.

ترتيب القراءة

يستخدم Viper الأولوية التالية لقراءة التكوين:

  1. إعداد القيم بشكل صريح
  2. علامات سطر الأوامر
  3. متغيرات البيئة
  4. ملفات التكوين
  5. تخزين القيم الرئيسية
  6. القيم الافتراضية

TIP

المفاتيح في تكوين Viper غير حساسة لحالة الأحرف، وقد تصبح خيارًا في المناقشات المستقبلية.

القيم الافتراضية

يجب أن يدعم نظام التكوين الجيد إعداد القيم الافتراضية، ورغم أنها قد لا تكون ضرورية أحيانًا، إلا أنها ستكون مفيدة جدًا عندما لا يتم إعداد ملف التكوين، وفيما يلي مثال.

go
viper.SetDefault("filePath","./dir/img/usr")
viper.SetDefault("root","123456")

قراءة ملفات التكوين

يحتاج Viper إلى تكوين قليل جدًا ليعرف أين يبحث عن ملفات التكوين. يدعم Viper ملفات JSON و TOML و YAML و HCL و INI و envfile و JavaProperties. يمكن لـ Viper البحث في مسارات متعددة في نفس الوقت، لكن حاليًا يدعم مثيل Viper الواحد ملف تكوين واحد فقط. لا يقوم Viper بتعيين مسار بحث افتراضي، بل يترك القرار الافتراضي للتطبيق.

فيما يلي مثال على استخدام Viper لقراءة ملف تكوين، لا تحتاج لتحديد مسار كامل، لكن يجب توفير ملف تكوين واحد على الأقل عند الاستخدام.

go
func TestReadConfigFile(t *testing.T) {
   viper.SetConfigName("config.yml") // قراءة ملف تكوين باسم config، بدون تحديد امتداد ملف محدد
   viper.SetConfigType("yaml")       // عندما لا يتم تحديد امتداد ملف محدد، يجب تحديد نوع الملف
   viper.AddConfigPath("./")         // البحث في المجلد الحالي
   viper.AddConfigPath("$HOME/")     // استخدام المتغير
   viper.AddConfigPath(".")          // البحث في دليل العمل
   err := viper.ReadInConfig() // قراءة التكوين
   if err != nil {
      log.Fatalln(err)
   }
}

يمكن أيضًا معالجة حالة عدم العثور على ملف التكوين بشكل منفصل

go
if err := viper.ReadInConfig(); err != nil {
  if _, ok := err.(viper.ConfigFileNotFoundError); ok {
    // ملف التكوين غير موجود
  } else {
    // أنواع أخرى من الأخطاء
  }
}

فيما يلي جميع الدوال للوصول إلى التكوين

  • Get(key string) : interface{}
  • GetBool(key string) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]interface{}
  • GetStringMapString(key string) : map[string]string
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : map[string]interface{}

عند الوصول إلى التكوين المتداخل استخدم الفاصل . للوصول، على سبيل المثال:

{
  "server":{
    "database":{
      "url": "mysql...."
    }
  }
}

يمكن استخدام GetString("server.database.url") للوصول المتداخل

كتابة ملفات التكوين

يوفر Viper سلسلة من الدوال لتسهيل على المطورين كتابة التكوين المخزن وقت التشغيل في ملفات التكوين.

go
// WriteConfig تكتب التكوين في ملف التكوين الأصلي، إذا لم يكن موجودًا ستبلغ عن خطأ، وإذا كان موجودًا سيتم الكتابة فوقه
func WriteConfig() error { return v.WriteConfig() }

// SafeWriteConfig تكتب التكوين بأمان في ملف التكوين الأصلي، إذا لم يكن موجودًا سيتم الكتابة، وإذا كان موجودًا لن يتم الكتابة فوقه
func SafeWriteConfig() error { return v.SafeWriteConfig() }

// WriteConfigAs تكتب التكوين الحالي في الملف المحدد، إذا لم يكن الملف موجودًا ستعيد خطأ، وإذا كان موجودًا ستكتب فوق التكوين الأصلي
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }

// SafeWriteConfigAs إذا كان الملف المحدد موجودًا، لن يتم الكتابة فوق ملف التكوين الأصلي، وإذا كان الملف موجودًا سيعيد خطأ
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }

فيما يلي بعض الأمثلة:

go
func TestWritingConfig(t *testing.T) {
   viper.WriteConfig() // كتابة التكوين في ملف التكوين الأصلي، يجب تعريف هذه الملفات مسبقًا بواسطة 'viper.AddConfigPath()' و 'viper.SetConfigName'
   viper.SafeWriteConfig()
   viper.WriteConfigAs("/path/to/my/.config")
   viper.SafeWriteConfigAs("/path/to/my/.config") // سيعيد خطأ لأن الملف المحدد موجود
   viper.SafeWriteConfigAs("/path/to/my/.other_config")
}

مراقبة وإعادة تحميل التكوين

يسمح Viper للتطبيقات بقراءة ملف تكوين ديناميكيًا وقت التشغيل، أي بدون الحاجة لإعادة تشغيل التطبيق لتفعيل التكوين المحدث، دون تفويت أي تفاصيل التغيير. فقط أخبر مثيل Viper بمراقبة تغييرات التكوين، أو يمكنك توفير دالة لـ Viper لتشغيلها في كل مرة يحدث فيها تغيير.

go
func TestWatchingConfig(t *testing.T) {
  viper.OnConfigChange(func(e fsnotify.Event) {
    fmt.Println("تم تغيير ملف التكوين:", e.Name)
  })
  viper.WatchConfig()
}

الأسماء المستعارة

go
func TestAliases(t *testing.T) {
   viper.RegisterAlias("a", "b")
   viper.Set("a", 1)
   viper.Set("b", 2) // سيكتب فوق تكوين a
   fmt.Println(viper.GetInt("a"))
}

استخراج البنية الفرعية

ذكرنا سابقًا استخدام الفاصل . للوصول إلى التكوين المتداخل، ويمكن أيضًا استخدام الدالة viper.Sub() لاستخراج البنية الفرعية، وقيمتها المرجعة هي مثيل Viper، كالمثال التالي:

yaml
cache:
  cache1:
    max-items: 100
    item-size: 64
  cache2:
    max-items: 200
    item-size: 80
go
cache1Config := viper.Sub("cache.cache1")
if cache1Config == nil { // إذا لم يكن موجودًا يعيد nil
  panic("تكوين cache1 غير موجود")
}

تعيين فاصل التداخل

عندما تريد تحديد مفتاح يحتوي على .، يجب عليك تحديد فاصل آخر يدويًا لمنع التحليل الخاطئ، على سبيل المثال:

go
viper.KeyDelimiter("/") // تعيين الفاصل إلى /

إلغاء التسلسل

يوفر Viper دالتين لإلغاء تسلسل التكوين إلى بنية أو خريطة، ويدعم أيضًا البنى المتداخلة:

  • Unmarshal(rawVal interface{}) : error
  • UnmarshalKey(key string, rawVal interface{}) : error
go
type config struct {
  Port int
  Name string
  PathMap string `mapstructure:"path_map"`
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
  t.Fatalf("تعذر إلغاء التسلسل إلى بنية, %v", err)
}

التسلسل

تسلسل التكوين الحالي إلى سلسلة بصيغة محددة لتخزينها في ملف تكوين، عادةً يدعم JSON, TOML, YAML, HCL, envfile، Java properties,

TIP

يدعم Viper أيضًا صيغ التسلسل المخصصة، Decoding custom formats with Viper - Márk Sági-Kazár (sagikazarmark.hu)

go
import (
  yaml "gopkg.in/yaml.v2"
  // ...
)

func yamlStringSettings() string {
  c := viper.AllSettings()
  bs, err := yaml.Marshal(c)
  if err != nil {
    log.Fatalf("تعذر تسلسل التكوين إلى YAML: %v", err)
  }
  return string(bs)
}

مثيلات متعددة

عادةً يكفي استخدام المثيل العام الذي يوفره Viper، لكن نظرًا لأن المثيل الواحد يمكنه تعيين ملف تكوين واحد فقط، يمكن إنشاء مثيلات متعددة لتنفيذ المزيد من العمليات، على سبيل المثال:

go
x := viper.New()
y := viper.New()

x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")

//...

Golang تم تحريره بواسطة www.golangdev.cn