"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > مثال عملي للمكتبات المشتركة في monorepo

مثال عملي للمكتبات المشتركة في monorepo

تم النشر بتاريخ 2024-11-09
تصفح:454

A practical example of shared libraries in a monorepo

أحد أقوى جوانب العمل في monorepo هو القدرة على مشاركة التعليمات البرمجية بين الحزم/الفرق/التسلسلات الهرمية. سأحاول في هذا المنشور شرح سيناريو بسيط جدًا للعالم الحقيقي

السيناريو المثال

تخيل أنك تريد تطوير مكتبة لإظهار أحجام الملفات بالميجابايت والتي تشعر أنها قد تكون مفيدة لأجزاء أخرى من monorepo الخاص بك. تقبل المكتبة الحجم كعدد صحيح (على سبيل المثال: 2048 بايت) ويمكنها إرجاع سلسلة متوافقة مع البشر (على سبيل المثال: 2 ميجابايت). ولإضافة بعض ضمان الجودة، سنكتب أيضًا اختبارًا لذلك.

كيف يقوم Bazel بتمكين رمز المشاركة؟

من السيناريو أعلاه، ندرك أننا بحاجة إلى تطوير هذه الوظيفة كمكتبة مشتركة يتم استيرادها بعد ذلك بواسطة حزمة أخرى للاستخدام. يجعل Bazel هذا الأمر بسيطًا للغاية من خلال السماح لنا بتحديد الوظيفة في المكتبة وتصديرها إلى الخدمات الأخرى التي ستحتاج إليها. كما هو موضح في مشاركتي السابقة المرتبطة أسفل هذا المنشور، يمكننا أيضًا التحكم في المكتبات الأخرى التي يمكن السماح لها باستيرادها للاستخدام أيضًا.

دعونا نحصل على الترميز

لأغراض تنظيم التعليمات البرمجية، سيكون لدينا دليل مكتبات في جذر مساحة العمل الخاصة بنا مع دليل فرعي يسمى humanize_filesize وهو المكان الذي سنكتب فيه رمز مكتبتنا.

دعنا نكتب بعض أكواد Go الأساسية جدًا في humanize_filesize.go

package humanize_filesize

import "fmt"

// GetHumanizedFilesize takes size_in_bytes as an int32 pointer and returns the size in megabytes.
func GetHumanizedFilesize(size_in_bytes *int32) string {
    if size_in_bytes != nil {
        size_in_megabytes := float64(*size_in_bytes) / (1024 * 1024)
        return fmt.Sprintf("%.4f MB", size_in_megabytes)
    }
    return "0 MB"
}

يأخذ هذا الرمز ببساطة int32 كمدخل ويعيد سلسلة ميغابايت محسوبة وقابلة للقراءة إلى 4 دقة عشرية

هذه الوظيفة ليست شاملة بالتأكيد ويمكن تحسينها بالتأكيد، ولكن هذا ليس الهدف من هذا التمرين.

تأكد أيضًا من أن منطقنا يعمل على النحو المنشود، وسنضيف اختبارًا أوليًا للغاية إلى جانب رمز go الخاص بنا في ملف يسمى humanize_filesize_test.go

package humanize_filesize

import (
    "testing"
)

func TestHumanizeFilesize(t *testing.T) {
    tests := []struct {
        name          string
        size_in_bytes *int32
        expected      string
    }{
        {
            name:          "nil bytes",
            size_in_bytes: nil,
            expected:      "0 MB",
        },
        {
            name:          "2048 bytes",
            size_in_bytes: int32Ptr(2048),
            expected:      "0.0020 MB",
        },
        {
            name:          "0 bytes",
            size_in_bytes: int32Ptr(0),
            expected:      "0.0000 MB",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := GetHumanizedFilesize(tt.size_in_bytes)
            if result != tt.expected {
                t.Errorf("expected %s, got %s", tt.expected, result)
            }
        })
    }
}

func int32Ptr(n int32) *int32 {
    return &n
}

اختبار بسيط جدًا يتضمن اختبارات أساسية لـ nil وint32 و0 كمدخلات

الآن يأتي الجزء المثير من كيفية تصدير هذه الوظيفة بحيث يمكن استيرادها ضمن حزم أو خدمات أخرى. هذا هو المكان الذي يتعين علينا فيه تحديد ملف BUILD.bazel.

load("@rules_go//go:def.bzl", "go_library", "go_test")

go_library(
    name = "humanize_filesize",
    srcs = ["humanize_filesize.go"],
    importpath = "basil/libraries/humanize_filesize",
    visibility = ["//visibility:public"],
)

go_test(
    name = "humanize_filesize_test",
    srcs = ["humanize_filesize_test.go"],
    embed = [":humanize_filesize"],
)

نحن هنا نحدد قاعدتين رئيسيتين. واحد للمكتبة الفعلية والآخر لملف الاختبار الذي كتبناه.

تحدد go_library أن الهدف humanize_filesize يستخدم humanize_filesize.go كأحد مصادره والذي يمكن استيراده من خلال المسار المحدد في importpath ويكون مرئيًا بشكل عام داخل مساحة العمل لاستيراد الحزم الأخرى. سوف نتعلم كيفية التحكم في الرؤية في منشور قادم.

يحدد go_test هدف اختبار يقوم بتضمين الكود من مخرجات go_library.

في هذه المرحلة يجب أن نكون قادرين على اختبار المكتبة عن طريق تشغيل مجموعة الاختبار الخاصة بنا على النحو التالي

بناء بازل //... && تشغيل بازل //libraries/humanize_filesize:humanize_filesize_test

يجب أن تكون قادرًا على رؤية نتيجة الاختبار على النحو التالي للإشارة إلى اجتياز جميع الاختبارات.

INFO: Analyzed target //libraries/humanize_filesize:humanize_filesize_test (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //libraries/humanize_filesize:humanize_filesize_test up-to-date:
  bazel-bin/libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
INFO: Elapsed time: 0.392s, Critical Path: 0.24s
INFO: 5 processes: 1 internal, 4 darwin-sandbox.
INFO: Build completed successfully, 5 total actions
INFO: Running command line: external/bazel_tools/tools/test/test-setup.sh libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //libraries/humanize_filesize:humanize_filesize_test
-----------------------------------------------------------------------------
PASS

؟ ووهو!!! ؟ الآن نحن نعلم أن مكتبتنا تعمل على النحو المنشود.

الآن دعنا نستخدم هذه المكتبة في خدمة Service1 ضمن دليل الخدمات الذي سنقوم بإنشائه في جذر مساحة العمل باستخدام كود go التالي وملف BUILD.bazel.

service1.go

package main

import (
    "basil/libraries/humanize_filesize"
    "fmt"
    "math/rand"
)

func main() {
    v := rand.Int31n(1000000)
    fmt.Printf(`%d bytes = %s\n`, v, humanize_filesize.GetHumanizedFilesize(&v))
}

BUILD.bazel

load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
    name = "service1_lib",
    srcs = ["service1.go"],
    importpath = "basil/services/service1",
    visibility = ["//visibility:private"],
    deps = ["//libraries/humanize_filesize"],
)

go_binary(
    name = "service1",
    embed = [":service1_lib"],
    visibility = ["//visibility:public"],
)

رمز الانتقال بسيط للغاية حيث يقوم باستيراد مكتبتنا التي أعلنا عنها سابقًا ويستخدم وظيفة GetHumanizedFilesize من مكتبتنا ويمرر قيمة عددية عشوائية ويطبع المخرجات.

الآن عند تنفيذ bazel build //services/service1، سيحل bazel جميع التبعيات لهدفنا بما في ذلك المكتبة التي قمنا بتطويرها وبنائها.

يمكن الآن تنفيذ

service1 باستخدام bazel run //services/service1 نظرًا لأن لدينا هدفًا ثنائيًا واحدًا محددًا فقط. إذا كان لديك أكثر من هدف ثنائي واحد، على سبيل المثال: ServiceX، فيمكنك تنفيذ ذلك باستخدام bazel run //services/service1:serviceX. افتراضيًا، عند عدم تحديد هدف، سيحاول bazel دائمًا العثور على هدف ثنائي يحمل نفس اسم الدليل وتشغيله.

إذن... ها أنت ذا. لقد أنشأنا أول مكتبتك المشتركة التي يمكن استخدامها من قبل أجزاء أخرى من monorepo لدينا.

يمكن العثور على جميع التعليمات البرمجية لهذا المثال على https://github.com/nixclix/basil/pull/3/commits/61c673b8757860bd5e60eb2ab6c35f3f4da78c87

إذا أعجبك محتوى هذه التدوينة فلا تتردد في مشاركتها. يرجى أيضًا الاشتراك وترك تعليقات حول رأيك في هذا المنشور وإذا كانت هناك أشياء ترغب في تحسينها.

بيان الافراج تم إعادة إنتاج هذه المقالة على: https://dev.to/nikhildev/a-practical-example-of-shared-libraries-in-a-monorepo-2548?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3