بعد سنوات من ملء الاستمارات والانتظار، حصلت أخيرًا على رخصتك المصرفية. هذا يعني أنك أصبحت الآن مؤهلًا رسميًا لفتح بنكك الخاص، هنيئًا لك!
أولويتك الأولى هي تشغيل أنظمة تقنية المعلومات. وبعد يوم من العمل الشاق، أصبحت قادرًا بالفعل على فتح الحسابات وإغلاقها، وعلى التعامل مع عمليات السحب والإيداع.
ولأنك لم ترغب في تكبّد عناء كتابة الاختبارات، دعوت بعض أصدقائك لمساعدتك في اختبار النظام. لكن بعد خمس دقائق فقط، ادّعى أحد أصدقائك أنه فقد المال! ورغم ثقتك بأن الكود الخاص بك خالٍ من الأخطاء، بدأت تتصفح السجلات للتحقيق.
آه أجل، كما توقعت تمامًا، صديقك هو المخطئ! فقد شارك بيانات اعتماده للاختبار مع صديق آخر، وتآمرا معًا على تنفيذ عمليات إيداع وسحب من الحساب نفسه على التوازي. ومن قد يفعل شيئًا كهذا؟
وبينما تجادل بأنه من المستحيل فعليًا أن يصل أحدهم إلى حسابه على التوازي، يخبرك صديقك بثقة متعالية أن قواعد العمل المصرفي تقتضي أن تدعم ذلك. إذن، لا دعم للعمليات المصرفية المتوازية، ولا إشارة انطلاق. فتُنهِد وتدوّن في ذهنك أن تعمل على هذا غدًا. سيؤخر ذلك تاريخ إطلاقك يومًا آخر على الأقل، ولكن حسنًا...
مهمتك هي تنفيذ حسابات بنكية تدعم الفتح والإغلاق والسحب والإيداع.
بما أن الحسابات البنكية يمكن الوصول إليها بطرق مختلفة (الإنترنت، والهواتف المحمولة، والخصومات التلقائية)، يجب أن يسمح برنامجك البنكي بالوصول إلى الحسابات بأمان من خيوط/عمليات متعددة (تختلف المصطلحات باختلاف لغة البرمجة لديك) في الوقت نفسه. على سبيل المثال، قد تحدث عمليات إيداع وسحب كثيرة في الوقت نفسه؛ عليك أن تضمن عدم وجود حالات تسابق بين قراءة رصيد الحساب وضبط الرصيد الجديد.
يجب أن يكون من الممكن إغلاق الحساب؛ فالعمليات على حساب مُغلق يجب أن تفشل.
في كود Roc، نركّز عادةً على منطق العمل بينما تتولى المنصة التعامل مع التزامن والحالة المشتركة.
لذلك، في هذا التمرين تحتاج فقط إلى تنفيذ عمليات الحساب في BankAccount.roc دون أن تقلق بشأن التزامن.
تبدأ الحسابات مغلقة، وإعادة فتح الحساب تعيد ضبط الرصيد، ويجب أن تكون الإيداعات والسحوبات موجبة.
يُظهر الملف bank-account-demo.roc المرفق كيف يمكن استخدام تنفيذك في خدمة ويب صغيرة.
يمكنك قراءته في المحرر، لكنه مستقل عن الاختبارات وليس جزءًا من حلك.
لتشغيل العرض التجريبي، عليك أن تثبّت Roc على جهازك، وأن تثبّت Exercism CLI، ثم تنفّذ الأمر التالي في الطرفية:
exercism download --exercise=bank-account --track=roc
بعد ذلك، نفّذ BankAccount.roc واختبره بالأمر التالي (غيّر مكان التنزيل الذي تطبعه واجهة سطر الأوامر إن اختلف عن المسار أدناه):
cd ~/Exercism/roc/bank-account
roc test bank-account-test.roc
بعد نجاح جميع الاختبارات، يمكنك تشغيل خادم العرض التجريبي بالأمر التالي:
roc bank-account-demo.roc
يستخدم الخادم منصة basic-webserver ودعمها المدمج لـ SQLite (تنزّل Roc المنصة عند التشغيل الأول).
لا حاجة إلى خادم قاعدة بيانات منفصل.
يستمع الخادم على http://127.0.0.1:8000 وينشئ الملف bank-account-demo.db في مجلدك الحالي (اضبط DB_PATH لاستخدام ملف قاعدة بيانات مختلف).
أوقف الخادم بـ Ctrl-C.
تبقى البيانات محفوظة بعد إعادة التشغيل.
في طرفية أخرى، يمكنك استدعاء خدمة الويب باستخدام curl (على Windows، استخدم curl.exe في كل الأوامر لتجنّب الاسم المستعار curl في PowerShell).
على سبيل المثال، هكذا تفتح الحساب رقم 123، وتودع 50، وتسحب 10، وتتحقق من رصيده:
curl -X POST http://127.0.0.1:8000/accounts/123/open
curl -X POST http://127.0.0.1:8000/accounts/123/deposit/50
curl -X POST http://127.0.0.1:8000/accounts/123/withdraw/10
curl http://127.0.0.1:8000/accounts/123/balance
سيكون الرصيد 40.
يجب أن تكون معرّفات الحسابات والمبالغ أعدادًا صحيحة غير سالبة.
تُرجع التحديثات OK عند النجاح، أو خطأً صادرًا من تنفيذ حسابك عند الفشل.
لإغلاق الحساب رقم 123:
curl -X POST http://127.0.0.1:8000/accounts/123/close
لتجربة طلبات متزامنة على Linux/macOS، استخدم معرّف حساب جديد وأرسل 100 إيداع من ثمانية عملاء:
curl -X POST http://127.0.0.1:8000/accounts/456/open
seq 100 | xargs -P 8 -I '{}' curl --fail --silent --show-error -X POST http://127.0.0.1:8000/accounts/456/deposit/1
curl http://127.0.0.1:8000/accounts/456/balance
على Windows، استخدم PowerShell 7+ للمثال المتوازي:
curl.exe -X POST http://127.0.0.1:8000/accounts/456/open
1..100 | ForEach-Object -Parallel { curl.exe --fail --silent --show-error -X POST http://127.0.0.1:8000/accounts/456/deposit/1 } -ThrottleLimit 8
curl.exe http://127.0.0.1:8000/accounts/456/balance
ينبغي أن تنجح جميع الإيداعات وأن يكون الرصيد 100.
يقرأ كل طلب الحساب، ويستدعي دوالك في Roc، ويحفظ النتيجة داخل معاملة Immediate في SQLite.
تُلغى العمليات الفاشلة فلا يطرأ أي تغيير على الحساب.
تسمح SQLite بكاتب واحد في كل مرة، ما يمنع الطلبات المتزامنة من الكتابة فوق تحديثات بعضها (حتى عبر حسابات مختلفة).
ملاحظة: يستخدم التمرين U64، لكن العرض التجريبي يخزّن الأرصدة كأعداد صحيحة في SQLite ويرفض التحديثات التي تتجاوز القيمة العظمى لـ I64 (9223372036854775807).
للاطلاع على مثال لتشغيل حسابات مستقلة بالتوازي، جرّب تمرين Parallel Letter Frequency.
استخدم match للتمييز بين Closed وOpen(balance).
تُرجع العملية حسابًا محدَّثًا بدلًا من تعديل الحساب الذي مُرّر إليها.
استخدم Ok(updated_account) للتحديث الناجح وErr(...) للعملية المرفوضة.
سجّل في Exercism لتتعلّم وتتقن Roc عبر 135 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.