السلام عليكم،
في 2021، بدأت أول وظيفة لي كمهندس برمجيات متدرب في الـ backend. وكانت الـ pre-commit hooks من أوائل الأدوات التي طلب مني senior engineers تثبيتها، لتراجع الكود تلقائيًا قبل حفظ تعديلاتي في Git.
وإلى اليوم، في داخلي شيء يكرهها. تنتهي من يوم طويل، وتحاول عمل commit، فتقرصك بملاحظة صغيرة كنت تتمنى تأجيلها. 😅 تصلحها، ثم تدرك أنها وفّرت عليك وعلى الفريق وقتًا في المراجعة.
وأثناء العمل على المصطلحات القرآنية، فكّرت في إضافة check للتسميات أيضًا، ينبّهنا إذا كتبنا اسمًا بطريقة تختلف عما اتفقنا عليه.
لماذا نحتاج إلى مراجعة المصطلحات؟
لنفترض أنك تعمل على تطبيق قرآني، وتريد كتابة متغيّر لرقم الآية. قد تسمّيه aya_number، بينما يكتب زميلك ayah_number، وتجد في مكتبة أخرى اسمًا هو verse_number.
كل هذه الأسماء تعني رقم الآية في المثال. لكنك ستحتاج إلى البحث بأكثر من صيغة لتجد مواضع استخدامها، وقد تحتار في الاسم الذي تختاره عندما تضيف كودًا جديدًا.
كان هذا أحد الأسئلة التي بدأت منها بحثي في تسميات المصطلحات القرآنية بين المشاريع. ومن البحث ونقاشات المجتمع، جمعنا جدولًا للمصطلحات نقترح فيه اسمًا لكل مفهوم، مثل ayah للآية وsurah للسورة، مع توضيح الأسماء البديلة.
واتفقنا من خلال هذا العمل على معيار لتسمية المصطلحات القرآنية في الكود، يحدد الأسماء وقواعد كتابتها. بعد هذا الويبينار، سننشر بإذن الله وثائق المعيار بصورة مستقلة، مع أمثلة للاستخدام وشرح لأسباب اختيار التسميات. والأداة التي أشارككم إياها هنا تساعدكم على تطبيقه في مشاريعكم.
كيف تحوّل المرجع إلى أداة؟
كانت الخطوة الأولى هي Quranic Vocab، واستفدت في تطويرها من مراجعات الأخ عبدالله عبيد ونقاشاته.
قدّمتها على شكل skill: ملف تعليمات ومرجع مصطلحات تستخدمهما الـ coding agents عندما تطلب منها مراجعة الأسماء في مشروعك. مثلًا، تلاحظ أنك كتبت aya_number وتقترح ayah_number وفق المرجع.
هذا يعني أن تثبّت الـ skill في الأداة التي تستخدمها، وتتذكر أن تطلب منها المراجعة. أردت أن تعمل مراجعة التسميات تلقائيًا مع كل commit.
والصراحة لا أذكر هل اقترح أحد أفراد المجتمع فكرة الـ pre-commit hook أم خطرت لي أثناء النقاش؛ ذاكرتي هذه الأيام لا تساعدني حتى في تذكّر ما تعشّيت أمس.
بنيت لهذا الغرض Quranic Terminology Lint. تراجع الأداة أسماء المصطلحات في الكود، وتوضح لك موضع الاسم الذي يحتاج إلى تعديل، والبديل المقترح، وقاعدة التسمية التي استندت إليها.
هذه deterministic checks، تطبّق قواعد التسمية المحددة في كل مرة. والقواعد موجودة داخل الأداة نفسها، لذلك تعمل على جهازك بعد التثبيت، من غير إنترنت أو coding agent.
أين تدخل الـ pre-commit hooks؟
الـ pre-commit hooks تعمل عند تنفيذ git commit. يشغّل Git الـ checks قبل إتمام العملية، لتراجع الملفات التي تريد إضافتها.
تجمع أداة pre-commit هذه الـ hooks وتثبّتها في المشروع. يمكنك مثلًا استخدام Prettier لتنسيق الكود، وRuff لمراجعته، وإضافة check للمصطلحات القرآنية معهما.
لنفترض أن لديك ملفًا اسمه example.py، تحفظ فيه رقم الآية ثم تطبعه:
aya_number = 5
print(aya_number)
عند محاولة عمل commit، يتوقف الـ check ويعرض الملاحظتين التاليتين:
example.py:1: error aya_number → ayah_number (rule 019: Ta marbutah outside idafah)
example.py:2: error aya_number → ayah_number (rule 019: Ta marbutah outside idafah)
كل سطر يوضح اسم الملف ورقم السطر، ثم الاسم الحالي والبديل المقترح. هنا يقترح ayah_number في موضعي استخدامه، وفق قاعدة كتابة التاء المربوطة في المعيار.
الملف نفسه لم يتغيّر. تصحح الاسم في السطرين، ثم تضيف التعديل باستخدام git add example.py وتعيد الـ commit.

تعمل هذه الـ checks على جهازك كما في الرسم. وبعد رفع الكود وفتح Pull Request، يراجعه الفريق وتعمل checks أخرى على GitHub بحسب إعداد المشروع.
وأعتقد أنها مفيدة أيضًا إذا كنت تستخدم coding agents: الأسماء التي تكتبها تمرّ على قواعد التسمية نفسها التي تراجع ما تكتبه أنت.
كيف أضيفه إلى مشروعي؟ 🛠️
تحتاج إلى Python 3.10 أو أحدث، وإلى تثبيت أداة pre-commit.
بعد ذلك، أنشئ ملفًا باسم .pre-commit-config.yaml في المجلد الرئيسي للمشروع، وضع فيه:
repos:
- repo: https://github.com/realabdu/quranic-terminology-lint
rev: v0.3.0
hooks:
- id: quranic-terminology
بهذا تحدد مستودع الأداة وإصدارها، وتختار الـ hook الذي يراجع المصطلحات. وإذا كان الملف موجودًا عندك، أضف المستودع إلى قائمة repos الموجودة فيه.
شغّل الأمر التالي من داخل المشروع:
pre-commit install
من الآن، ستعمل الـ checks عند كل commit على الملفات التي جهّزتها له باستخدام git add.
ولمراجعة ملفات المشروع الموجودة أصلًا، يمكنك تشغيل:
pre-commit run quranic-terminology --all-files
أنصح بالبدء بهذا الأمر وقراءة النتائج، خصوصًا في مشروع قديم. الإعداد السابق يعرض الملاحظات من دون تعديل الملفات. وإذا وجد خطأ في التسمية، يوقف الـ commit حتى تعالجه أو تسجّل له استثناءً مناسبًا. أما التحذيرات فلا توقفه.
هل يمكنه تعديل الأسماء؟
الإعداد السابق يشغّل check فقط: يقترح التصحيحات ويترك الملفات كما هي. وإذا أردت تطبيق التصحيحات تلقائيًا، يمكنك اختيار unsafe fix.
لنرجع إلى ملف example.py نفسه:
aya_number = 5
print(aya_number)
عند تشغيل unsafe fix، يصبح محتوى الملف:
ayah_number = 5
print(ayah_number)
في هذا المثال تغيّر اسم المتغيّر في تعريفه وفي السطر الذي يطبعه. لتفعيل هذا الخيار، استبدل إعداد الـ hook السابق بهذا الإعداد:
repos:
- repo: https://github.com/realabdu/quranic-terminology-lint
rev: v0.3.0
hooks:
- id: quranic-terminology-unsafe-fix
بعد حفظ الإعداد، يمكنك تجربته على الملف نفسه:
pre-commit run quranic-terminology-unsafe-fix --files example.py
ولتشغيله على جميع الملفات المتتبّعة في Git، استخدم --all-files بدل --files example.py. وسيعمل أيضًا تلقائيًا عند الـ commit على الملفات التي جهّزتها له.
وسمّيته unsafe لأن تغيير الاسم قد يؤثر في كود آخر. إذا كنت تستورد Chapters من مكتبة، فإن استبداله بـ Surahs قد يكسر الـ import؛ فالمكتبة ما زالت تستخدم الاسم القديم. الأداة تستبدل النصوص، ولا تتتبّع جميع العلاقات بين أجزاء الكود.
لذلك راجع الـ diff وشغّل الـ tests بعد التعديل، ثم أضف التغييرات باستخدام git add وأعد الـ commit. ويمكنك بدلًا من ذلك البقاء على check واستخدام ميزة Rename في المحرر لتطبيق الاقتراحات بنفسك.
يدعم unsafe fix أسماء الكود في Python وبعض صيغ JavaScript وTypeScript، مع تصحيحات في التعليقات والنصوص المدعومة. ويترك النصوص داخل strings وملفات البيانات وأسماء الملفات والصيغ التي لا يدعم تعديلها كما هي. لهذا قد تبقى بعض الملاحظات حتى بعد تشغيله.
يمكن أيضًا استثناء أسماء تفرضها مكتبة خارجية، أو ملفات بيانات تريد الاحتفاظ بها كما وصلت إليك. وتسمح الأداة في الشرح بأسماء مثل Tajweed، حتى لو كان المعيار يختار tajwid للمتغيرات.
وإذا ثبّتّ الأداة لتشغيلها مباشرة من الـ terminal، فهذان هما الأمران للملف نفسه:
# Check only
quranic-terminology-lint example.py
# Apply unsafe fixes
quranic-terminology-lint example.py --unsafe-fixes
تجد طريقة التثبيت المباشر وتفاصيل الصيغ المدعومة والاستثناءات في توثيق الأداة.
ماذا حدث عند تجربته على المكتبات؟
قبل نشر الإصدار v0.3.0، جرّبت الوضعين على نسخ محددة من ثلاث مكتبات قرآنية مفتوحة المصدر. في وضع check لم يتغيّر أي ملف في المشاريع الثلاثة. أما عند تفعيل unsafe fix:
- في quran-meta، طبّقت الأداة 1927 استبدالًا في 86 ملفًا.
- في PyQuran، طبّقت 667 استبدالًا في 12 ملفًا.
الأداة مفتوحة المصدر برخصة MIT. أتمنى أن تسهّل عليكم الالتزام بالمعيار أثناء التطوير. وإذا جرّبتموها ووجدتم اقتراحًا لا يناسب الكود عندكم، شاركونا مثالًا صغيرًا يوضح الحالة لنحسّن الأداة ونعرف أين نحتاج إلى استثناء.