اذهب إلى المحتوى

كل الأنشطة

تحدث تلقائيًا

  1. الساعة الماضية
  2. لا تحتاج إلى استخدام not لأنك تتحقق بالفعل مما إن كان الاسم موجودًا باستخدام in. فـ in نستخدمها للتحقق من عنصر معين وهو name لتفقد إن كان موجودًا في تسلسل معين (مثل قائمة employees)، وإن وجد العنصر، تعود العبارة بقيمة صحيحة True. أما not in فهي لتفقد مما إن كان عنصر معين غير موجود في تسلسل معين، وإن لم يوجد تعود العبارة بقيمة صحيحة True. لذا بما أنك تريد التحقق من الاسم هل موجود في قائمة الموظفين، فإن استخدام in هو الصحيح، ولو تريد التحقق مما إن كان الاسم غير موجود، فنستخدم not in. if name not in employees: print(f"The name is not: {name}") else: print("The name is: " + name)
  3. if name in employees : print("The name is: " + name) else: print(f"The name not : {name}") ما الفرق هنا بين not او in هنا استطعت التأكد من الموظف موجود ام لا بدون استخدام not لماذ استخدم not
  4. اليوم
  5. ننهي في هذا المقال اﻷخير من سلسلتنا ما بدأناه في إنشاء واجهة برمجية REST باستخدام Express.js، ونناقش فيه موضوع السماحيات permissions والاختبارات المؤتمتة لعمل التطبيق. سماحيات المستخدم بعد التحقق من هوية مستثمر الواجهة البرمجية، علينا أن نعرف إن كان مسموحًا لهذا المستثمر الوصول إلى الموارد التي تتيحها الواجهة أم لا. ويشيع استخدام مجموعة من السماحيات لكل مستخدم، إذ يقدّم هذا اﻷسلوب طريقة أكثر مرونة من استراتيجية مستوى الوصول access level وأقل تعقيدًا. وبصرف النظر عن منطق عمل كل سماحية (تصريح)، من اﻷوضح إتباع طريقة عامة للتعامل معها. عامل مقارنة البتات AND (&) وقوة العدد 2 نستخدم العامل & المضمّن في جافا سكريبت ﻹدارة السماحيات. إذ نخزّن كل التصريحات ضمن كيان واحد ورقم واحد يخص كل مستخدم. ثم باستخدام التمثيل الثنائي (0100 للرقم 4 مثلًا) للرقم الخاص بالمستخدم والعامل & يمكننا معرفة التصريحات المعطاة له. لا تعر اهتمامًا كبيرًا بالرياضيات طالما أن الفكرة سهلة الاستخدام. نعرّف كل نوع من التصريحات (رايات سماحية permission flags) على شكل قوّة للرقم 2 أي: 2, 4, 8, 16… ، وهكذا حتى نحصل على حد أعظمي مكوّن من 31 راية. لنأخذ مثالًا عمليًا عن مدوّنة تقدم محتوى صوتي إضافة إلى النصي تُعطى فيها السماحيات التالية: 1: مؤلف ويمكنه التعديل على النص. 2: مصوّر ويمكنه التعديل على الصور. 4: معلّق ويمكنه تغيير الملف الصوتي الخاص بكل فقرة نصية. 8: مترجم يمكنه التعديل على الترجمات. وباستخدام النهج السابق يمكن إعطاء كل أنواع السماحيات للمستخدمين، وإليك أمثلة عن ذلك: المستخدم له صلاحية تعديل النص:سيُمنح الرقم 1 فقط. المستخدم له صلاحية تعديل النص والتعليق عليه: سيُمنح الرقم 5=1+4. المستخدم له صلاحية تعديل النص والصور: سيُمنح الرقم 3=2+1 المستخدم له صلاحية تعديل النص والصور والترجمة: سيُمنح الرقم 11=1+2+8 المستخدم مدير له جميع الصلاحيات الحالية والمستقبلية: سيُمنح الرقم 2,147,483,647 وهو أعلى عدد صحيح مكوّن من 32 بت. لكن كيف سيجري اﻷمر؟ لنفرض الحالة التي يحمل فيها المستخدم الرقم 12 الذي يُمثّل ثنائيًا كالتالي 00001100، ويحاول التعليق الصوتي ذو التصريح رقم 4 الذي يُمثل ثنائيًا كالتالي 00000100. نستخدم اﻵن العملية & على الرقمين السابقين كالتالي: 00001100 & 00000100 ستكون النتيجة 00000100 وذلك بمقارنة قيمة كل خانة من الأول مه مقابلتها في الثاني ووضع النتيجة 1 إذا حملت الخانتين القيمة 1 وصفر فيما عدا ذلك. إن النتيجة هي الرقم 4 وبالتالي يُسمح له بالتعليق الصوتي. نطبق نفس الخوارزمية على رقم المستخدم والصلاحية التي يريد استخدامها فإن كانت نتيجة العملية & هي 0 يُمنع من استخدام الصلاحية وإلا سيُسمح له باستخدامها (جرّب أرقامًا أخرى!). كتابة شيفرة رايات السماحيات نخزن رايات السماحيات ضمن المجلد common لأن منطق العملية قد يتكرر في وحدات أخرى مستقبلًا، ثم ننشئ الملف common/middleware/common.permissionflag.enum.ts ليحمل قيم بعض الرايات: export enum PermissionFlag { FREE_PERMISSION = 1, PAID_PERMISSION = 2, ANOTHER_PAID_PERMISSION = 4, ADMIN_PERMISSION = 8, ALL_PERMISSIONS = 2147483647, } ملاحظة: طالما أن التطبيق هو مجرد مثال، اخترنا أن تكون أسماء الصلاحيات معممة. لنعد اﻵن إلى الدالة ()addUser ضمن كائن DAO لاستبدال الرقم 1 بالقيمة PermissionFlag.FREE_PERMISSION بعد إدراج الملف السابق في مكان مناسب. كما يمكن إدراج هذا الملف ضمن ملف جديد لوحدة وسيطة تضم صنف متفرّد يُدعى PermissionFlag.FREE_PERMISSION: import express from 'express'; import { PermissionFlag } from './common.permissionflag.enum'; import debug from 'debug'; const log: debug.IDebugger = debug('app:common-permission-middleware'); وبدلًا من إنشاء عدة دوال وسيطة مشابهة، نستخدم نمط المصنع factory pattern ﻹنشاء مصنع للتوابع أو الدوال (أو ببساطة مصنع). تسمح لنا دالة المصنع توليد دوال وسيطة -عند تهيئة المسار- تتحقق من راية أية سماحية مطلوبة. وهكذا نتفادى تكرار الدوال الوسيطة عندما نضيف راية جديدة. إليك شيفرة المصنع الذي يوّلد الدوال الوسيطة التي تتحقق من رايات السماحيات: permissionFlagRequired(requiredPermissionFlag: PermissionFlag) { return ( req: express.Request, res: express.Response, next: express.NextFunction ) => { try { const userPermissionFlags = parseInt( res.locals.jwt.permissionFlags ); if (userPermissionFlags & requiredPermissionFlag) { next(); } else { res.status(403).send(); } } catch (e) { log(e); } }; } ويسمح لمستخدم الدخول إلى حساب معين إذا كان فقط حسابه الشخصي أو كان مديرًا: async onlySameUserOrAdminCanDoThisAction( req: express.Request, res: express.Response, next: express.NextFunction ) { const userPermissionFlags = parseInt(res.locals.jwt.permissionFlags); if ( req.params && req.params.userId && req.params.userId === res.locals.jwt.userId ) { return next(); } else { if (userPermissionFlags & PermissionFlag.ADMIN_PERMISSION) { return next(); } else { return res.status(403).send(); } } } نضيف اﻵن دالة وسيطة جديدة إلى الملف users.middleware.ts: async userCantChangePermission( req: express.Request, res: express.Response, next: express.NextFunction ) { if ( 'permissionFlags' in req.body && req.body.permissionFlags !== res.locals.user.permissionFlags ) { res.status(400).send({ errors: ['User cannot change permission flags'], }); } else { next(); } } وطالما أن الدالة السابقة تعتمد على القيمة res.locals.user، باﻹمكان نشرها ضمن الدالة ()validateUserExists قبل استدعاء ()next: // ... if (user) { res.locals.user = user; next(); } else { // ... إن ما فعلناه ضمن الدالة ()validateUserExists ينفي ضرورة تنفيذه ضمن الدالة ()validateSameEmailBelongToSameUser، لهذا يمكن حذف استدعاء قاعدة البيانات فيه واستبداله بالقيمة التي يمكن نضمّن أنها مخزّنة في res.locals: - const user = await userService.getUserByEmail(req.body.email); - if (user && user.id === req.params.userId) { + if (res.locals.user._id === req.params.userId) { نستطيع اﻵن ربط منطق تحديد السماحيات بالملف users.routes.config.ts. طلب التصريحات ندرج بداية اﻷداة الوسيطة الجديدة والملف enum: import jwtMiddleware from '../auth/middleware/jwt.middleware'; import permissionMiddleware from '../common/middleware/common.permission.middleware'; import { PermissionFlag } from '../common/middleware/common.permissionflag.enum'; ما نريده ألا تُعرض قائمة المستخدمين إلا لمستخدم ذو صلاحيات مدير، لكننا نريد أيضًا أن يكون إنشاء مستخدم جديد متاحًا للعموم، كما تجري اﻷمور اعتياديًا. دعونا بداية نحدد سماحيات الوصول إلى قائمة المستخدمين عن طريق دالة المصنع قبل المتحكم: this.app .route(`/users`) .get( jwtMiddleware.validJWTNeeded, permissionMiddleware.permissionFlagRequired( PermissionFlag.ADMIN_PERMISSION ), UsersController.listUsers ) // ... وتذكّر أن استدعاء دالة المصنع يُعيد دالة وسيطة، وبالتالي تُعيد مرجعًا إلى الدوال الوسيطة اﻷخرى التي لا تنتج عنها دون أن تُنفّذ. من القيود اﻷخرى هو منع الوصول إلى المسارات التي تضم معرّف المستخدم userId، ما لم يكن صاحب التصريح هو صاحب المعرّف أو المدير: .route(`/users/:userId`) - .all(UsersMiddleware.validateUserExists) + .all( + UsersMiddleware.validateUserExists, + jwtMiddleware.validJWTNeeded, + permissionMiddleware.onlySameUserOrAdminCanDoThisAction + ) .get(UsersController.getUserById) وعلينا أيضًا منع المستخدمين من رفع مستوى سماحياتهم عن طريق إضافة القيمة UsersMiddleware.userCantChangePermission قبل المرجع إلى الدالة في نهاية الوجهات التي تقود إلى العمليتين PUT و PATCH. وسندعم أيضًا في الواجهة البرمجية منطق السماح للمستخدمين الذين يمتلكون التصريح PAID_PERMISSION فقط في تحديث بياناتهم، وقد لا يلائم هذه اﻷمر مشاريع أخرى، لكننا سننفذه هنا كي نميز بين السماحيات المدفوعة والمجانية. باﻹمكان تنفيذ اﻷمر بإضافة دالة مولّدة أخرى بعد كل مرجع أضفناه إلى التصريح userCantChangePermission: permissionMiddleware.permissionFlagRequired( PermissionFlag.PAID_PERMISSION ), وهكذا نكون مستعدين ﻹعادة تشغيل Node.js والتجريب. الاختبار اليدوي للتصريحات لنحاول بداية أن نحصل على قائمة المستخدمين دون مفتاح: curl --include --request GET 'localhost:3000/users' \ --header 'Content-Type: application/json' ستكون الاستجابة HTTP 401 لاننا بحاجة إلى مفتاح JWT صالح، لهذا سنعيد المحاولة باستخدام مفتاح الوصول الذي حصلنا عليه في مقالنا السابق: curl --include --request GET 'localhost:3000/users' \ --header 'Content-Type: application/json' \ --header "Authorization: Bearer $REST_API_EXAMPLE_ACCESS" وسنحصل هذه المرة أيضًا على الاستجابة HTTP 403 لأن المفتاح صالح لكن الوصول إلى نقطة الوصول هذه مرفوض لعدم امتلاك الطلب التصريح ADMIN_PERMISSION. ,وبالطبع لن نحتاج إلى تصريح كهذا للحصول على سجل المستخدم نفسه: curl --request GET "localhost:3000/users/$REST_API_EXAMPLE_ID" \ --header 'Content-Type: application/json' \ --header "Authorization: Bearer $REST_API_EXAMPLE_ACCESS" وستكون الاستجابة كالتالي: { "_id": "UdgsQ0X1w", "email": "marcos.henrique@toptal.com", "permissionFlags": 1, "__v": 0 } وبالمقابل، يُفترض أن يفشل طلب تحديث سجلات المستخدم نفسه لأن قيمة السماحية التي يمتلكها هي 1 (مجاني FREE_PERMISSION? curl --include --request PATCH "localhost:3000/users/$REST_API_EXAMPLE_ID" \ --header 'Content-Type: application/json' \ --header "Authorization: Bearer $REST_API_EXAMPLE_ACCESS" \ --data-raw '{ "firstName": "Marcos" }' والاستجابة كما نتوقع هي : HTTP 403. إن متطلبات استعلام الوجهة auth/ من خلال الطلب POST يُظهر سيناريو أمني لا بد من الانتباه إليه. فعندما يغير مالك الموقع سماحيات مستخدم، كأن يحاول إلغاء مستخدم يسئ السلوك، فلن يشعر المستخدم بذلك حتى يحصل تحديث على مفتاح JWT الذي يمتلكه، لأن التحقق من السماحيات يجري على بيانات مفتاح JWT لتقليل الوصول إلى قاعدة البيانات. تساعدك خدمات مثل Auth0 بتقديم تدوير تلقائي للمفاتيح، لكن مع ذلك، سيلاحظ المستخدمين سلوكًا غريبًا في الفترة التي تفصل بين عمليات التدوير، ويدوم لفترة قصيرة عادة. ولتخفيف هذه الظاهرة، على المطور أن ينتبه إلى ضرورة تنفيذ آلية تحديث المفاتيح كاستجابة لتغيّر السماحيات. ويجدر بمطوري الواجهات البرمجية REST الانتباه إلى الثغرات بتنفيذ الكثير من عمليات CRUD وبشكل دوري، لكن سرعان ما يصبح هذا الأمر مرهقًا ومنبعًا لمشاكل جديدة. الاختبارات المؤتمتة مع توسع وزيادة حجم وإمكانيات الواجهة البرمجية، سيصعب صيانتها والمحافظة على جودتها وخصوصًا مع التغيّر المستمر لمنطق العمل. ولتقليل الثغرات في الواجهة البرمجية بقدر المستطاع ونشر التغييرات الجديدة بثقة، يشيع استخدام أطقم برمجية لاختبار الواجهتين الأمامية والخلفية للتطبيقات. وبدلًا من الغوص في تفاصيل كتابة اختبارات وشيفرة اختبارات، سنعرض آليات أساسية تقدّم مجموعة اختبارت يمكن لنا البناء عليها. التعامل مع البيانات الناتجة عن الاختبارات قبل أن نؤتمت الاختبارات، من الجيد التفكير بما سيحدث للبيانات الناتجة عن الاختبارات. إذ نستخدم في تطبيقنا Docker Compose لتشغيل قاعدة البيانات المحلية، وتؤثر الاختبارات على قاعدة البيانات بترك مجموعة جديدة من السجلات بعد كل اختبار. لا يمثل هذا اﻷمر مشكلة في أغلب اﻷوقات، لكن إن رأيت أنه كذلك، لا بأس بتعديل الملف docker-compose.yml ﻹنشاء قاعدة بيانات جديدة لأغراض الاختبار. عادة ما يُجري المطورين اختباراتهم المؤتمتة في الواقع كجزء من خط التسليم المستمر. ولتنفيذ اﻷمر، من المنطقي إعداد طريقة ﻹنشاء قاعدة بيانات مؤقتة لتنفيذ كل اختبار. لهذا نستخدم Mocha و Chai و SuperTest لإنشاء اختباراتنا: npm i --save-dev chai mocha supertest @types/chai @types/express @types/mocha @types/supertest ts-node يدير Mocha تطبيقنا ويًجري الاختبارات، بينما يسهّل Chai قراءة تعابير الاختبارات، ويسهّل SuperTest اختبارات التكامل بين الواجهتين أو اختبارات طرف إلى طرف end-to-end بين الواجهة البرمجية RESTوعميل ريست. علينا بداية تعديل السكريبت package.json: "scripts": { // ... "test": "mocha -r ts-node/register 'test/**/*.test.ts' --unhandled-rejections=strict", "test-debug": "export DEBUG=* && npm test" }, تسمح لنا الشيفرة السابقة بتنفيذ الاختبارات في مجلد جديد ندعوه test. اختبار وصفي لتجربة هيكلية الاختبار، سننشئ الملف test/app.test.ts: import { expect } from 'chai'; describe('Index Test', function () { it('should always pass', function () { expect(true).to.equal(true); }); }); قد تبدو الصياغة غير مألوفة لكنها صحيحة. إذ نعرف الاختبار بتوقع سلوك معين ()expect ضمن الكتلة ()it التي تُستدعى بدورها ضمن الكتلة ()describe. ننفذ التعليمة التالية في الطرفية: npm run test ومن المفترض أن نرى النتيجة التالية: > mocha -r ts-node/register 'test/**/*.test.ts' --unhandled-rejections=strict Index Test ✓ should always pass 1 passing (6ms) وهكذا تكون مكتبات الاختبار جاهزة للعمل. تحضير الاختبارات للعمل ليبقى خرج الاختبارات واضحًا، علينا إيقاف عمل المكتبة winston التي تسجّل الطلبات كليًا خلال تنفيذ الاختبارات، لهذا غيّر الفرع else (ليس في وضع التنقيح طبعًا) في الملف app.ts للتحقق من وجود الدالة ()it من Mocha: if (!process.env.DEBUG) { loggerOptions.meta = false; // عندما لا نكون في وضع التنقيح + if (typeof global.it === 'function') { + loggerOptions.level = 'http'; // for non-debug test runs, squelch entirely + } } علينا أخيرًا تصدير الملف app.ts ليكون عرضة للاختبارات، لهذا سنضيف السطر export default في نهاية الملف app.ts قبل السطر ()server.listen مباشرة، لأن ()listen يعيد الكائن http.Server. تحقق من أن كل شيئ على ما يرام بتنفيذ اﻷمر: npm run test. الاختبار المؤتمت اﻷول على واجهتنا البرمجية حتى نبدأ إعداد الاختبارات على المستخدمين، سننشئ الملف test/users/users.test.ts ونبدؤه بإدراج المكتبات واﻹعتماديات اللازمة: import app from '../../app'; import supertest from 'supertest'; import { expect } from 'chai'; import shortid from 'shortid'; import mongoose from 'mongoose'; let firstUserIdTest = ''; // سيضم لاحقًا قيمة تعيدها الواجهة البرمجية const firstUserBody = { email: `marcos.henrique+${shortid.generate()}@toptal.com`, password: 'Sup3rSecret!23', };`()server.listen``()server.listen` let accessToken = ''; let refreshToken = ''; const newFirstName = 'Jose'; const newFirstName2 = 'Paulo'; const newLastName2 = 'Faraco'; ننشئ تاليًا الكتلة الخارجية ()describe مع بعض اﻹعدادات والتعريفات: describe('users and auth endpoints', function () { let request: supertest.SuperAgentTest; before(function () { request = supertest.agent(app); }); after(function (done) { //وأغلق الاتصال Express.js أغلق خادم shut down the Express.js //بأننا انتهينا Mocha ثم أخبر MongoDB مع app.close(() => { mongoose.connection.close(done); }); });will later hold a value returned by our API }); تُستدعى الدوال التي نمررها إلى ()before و ()after قبل وبعد كل الاختبارات التي نعرّفها عند استدعاء ()it ضمن نفس الكتلة ()describe. وللدالة التي نمررها إلى ()after دالة أخرى done نستدعيها بمجرّد انتهينا من إعادة التطبيق واتصاله مع قاعدة البيانات إلى الوضع الأساسي. ملاحظة: لو لم نستخدم الدالة ()after، ستتوقف Mocha عن الاستجابة بعد إكمال كل اختبار بنجاح. وننصح هنا باستدعاء Mocha دومًا مع الوسيط exit-- لتفادي هذا السلوك مع أنه ينطوي على ثغرة. فلو توقف الاختبار عن الاستجابة لأسباب أخرى، نتيجة وعد لم يُكتب بطريقة صحيحة في الاختبار أو التطبيق مثلًا، عندها لن تنتظر Mocha وتعطي رسالة بنجاح الاختبار حتى لو حدث خطأ في التطبيق مما يزيد تعقيد عملية تنقيح اﻷخطاء أصبحنا اﻵن جاهزين ﻹضافة اختبارات فردية طرف-إلى-طرف ضمن الكتلة ()describe: it('should allow a POST to /users', async function () { const res = await request.post('/users').send(firstUserBody); expect(res.status).to.equal(201); expect(res.body).not.to.be.empty; expect(res.body).to.be.an('object'); expect(res.body.id).to.be.a('string'); firstUserIdTest = res.body.id; }); تُنشئ هذه الدالة مستخدمًا جديدًا وفريدًا لأن البريد اﻹلكتروني للمستخدم قد ولد سابقًا باستخدام shortid. بضم المتغير request عميل SuperTest الذي يسمح لنا تنفيذ طلبات HTTP إلى الواجهة البرمجية. وعلى جميع الطلبات أن تستخدم await لهذا تكون جميع الدوال التي نمررها إلى ()it غير متزامنة async. ثم نستخدم بعد ذلك ()expect من Chai لاختبار مختلف نواحي النتيجة. جرب تنفذ اﻷمر اﻵن لترى كيف يعمل الاختبار. سلسلة من الاختبارات سنضيف جميع كتل ()it التالية ضمن الكتلة ()describe، ولا بد من إضافتها وفق الترتيب المعروض تاليًا كي تعمل جميعها مع المتغيّرات التي نغيّرها مثل firstUserIdTest: it('should allow a POST to /auth', async function () { const res = await request.post('/auth').send(firstUserBody); expect(res.status).to.equal(201); expect(res.body).not.to.be.empty; expect(res.body).to.be.an('object'); expect(res.body.accessToken).to.be.a('string'); accessToken = res.body.accessToken; refreshToken = res.body.refreshToken; }); نحضر هنا مفتاحي الوصول والتحديث الجديدين من المستخدم الذي أضيف مؤخرًا: it('should allow a GET from /users/:userId with an access token', async function () { const res = await request .get(`/users/${firstUserIdTest}`) .set({ Authorization: `Bearer ${accessToken}` }) .send(); expect(res.status).to.equal(200); expect(res.body).not.to.be.empty; expect(res.body).to.be.an('object'); expect(res.body._id).to.be.a('string'); expect(res.body._id).to.equal(firstUserIdTest); expect(res.body.email).to.equal(firstUserBody.email); }); يدفع ذلك الطلب GET من المسار userID: (والذي يحمل مفتاح التشفير) إلى التحقق من أن بيانات المستخدم في الاستجابة تتطابق مع ما أرسلناه أساسًا. الاختبارات المتداخلة وتجاوز الاختبارات وعزلها يمكن للكتل ()itفي Mocha أن تضم كتل ()describe، أي يمكن أن تتداخل الاختبارات، وهذا ما سنفعله في اختبارنا التالي. سيجعل ذلك الاعتماديات المتعاقبة أوضح في نواتج الاختبار كما سنراها في النهاية: describe('with a valid access token', function () { it('should allow a GET from /users', async function () { const res = await request .get(`/users`) .set({ Authorization: `Bearer ${accessToken}` }) .send(); expect(res.status).to.equal(403); }); }); لا تغطي الاختبارات الفعالة ما نتوقعه نجاحه فقط، بل ما نتوقعه أن يفشل أيضًا. إذ حاولنا هنا الحصول على قائمة بأسماء المستخدمين وتوقعنا أن تكون النتيجة الخطأ 403 لأن المستخدم الجديد سيحمل التصريح الافتراضي ولا يسمح له بمثل هذا الطلب إلى نقطة الوصول. وهكذا يمكننا الاستمرار في كتابة اختبارات ضمن الكتلة ()describe الجديدة. وطالما أننا ناقشنا الميزات التي استخدمناها في بقية شيفرة الاختبار، يمكنك الاطلاع عليها في المستودع الخاص بالتطبيق. مر مع تزوّدنا المكتبة Mocha بميزات عدة قد تجدها مفيدة أثناء تطوير وتنقيح الاختبارات منها: التابع ()skip:ويستخدم لتفادي اختبار وحيد أو كتلة كاملة من الاختبارات. فعندما نستبدل ()it بالدالة ()it.skip (وكذلك الأمر مع ()describe)، لن يعمل الاختبار أو الاختبارات محط الاهتمام بل ستؤجل ويشار إليها بالعبارة "بانتظار التنفيذ pending" في الخرج الأخير للمكتبة Mocha. التابع ()only: يُستخدم لتجاهل جميع الاختبارات غير الموسومة بالوسم only.، ولن يظهر أي شيء في الخرج على أنه "بانتظار التنفيذ". المعامل bail--: باﻹمكان استخدام هذا المعامل ضمن سطر تعريف Mocha في الملف package.json ﻹيقاف الاختبارات بمجرد فشل إحداها. ولهذا اﻷمر فائدة خاصة في تطبيق الواجهة البرمجية الذي نبنيه، لأننا أعددنا الاختبارات لتكون متعاقبة، وبالتالي عند فشل أول اختبار ستتوقف Mocha مباشرة بدلًا من المتابعة واﻹشارة إلى أخطاء في بقية الاختبارات المتعلقة بالاختبار الفاشل علمًا بأنها قد تكون ناجحة لكنها فشلت نتيجة لفشل أول اختبار. عند تنفيذ مجموعة اختباراتنا اﻵن باستخدام سطر اﻷوامر npm run test، سنلاحظ إخفاق ثلاث اختبارات (لو أردنا ترك الدوال الثلاث التي تعتمد على شيفرة غير منجزة حتى اللحظة، فمن المناسب جدًا لتطبيق التابع ()skip عليها). أخفقت الاختبارات الثلاث السابقة نظرًا لوجود قطعتين مفقودتين من شيفرة التطبيق حتى اللحظة، اﻷولى موجودة ضمن الملف npm run test: this.app.put(`/users/:userId/permissionFlags/:permissionFlags`, [ jwtMiddleware.validJWTNeeded, permissionMiddleware.onlySameUserOrAdminCanDoThisAction, //من الضروري وجود كتلتي الشيفرة السابقتين كجزء من الأداة الوسيطة //لأنها تغطي فقط .all() على الرغم من وجود مرجع إليهما ضمن الدوال // /users/:userId, وليس كل شيء تختها في الشجرة permissionMiddleware.permissionFlagRequired( PermissionFlag.FREE_PERMISSION ), UsersController.updatePermissionFlags, ]); وعلينا أن نعدّل ثانيًا الملف users.controller.ts لأننا وضعنا مرجعًا إلى دالة غير موجودة فيه. لهذا علينا إضافة السطر ب;import { PatchUserDto } from '../dto/patch.user.dto' بالقرب من أعلى الصفحة وإضافة الدالة المفقودة إلى الصنف: async updatePermissionFlags(req: express.Request, res: express.Response) { const patchUserDto: PatchUserDto = { permissionFlags: parseInt(req.params.permissionFlags), }; log(await usersService.patchById(req.body.id, patchUserDto)); res.status(204).send(); } إن إضافة هذه اﻹمكانيات مفيد في حالات الاختبار لكنه لن يناسب معظم الاحتياجات في التطبيقات الحقيقية، لهذا نترك لك التمرينين التاليين للعمل عليهما: فكر بطرق تمنع فيها الشيفرة مجددًا المستخدم من تغيير رايات السماحية permissionFlags الخاصة به وتسمح مع ذلك باختبار نقاط الوصول المقيّدة بالتصريحات. أنشئ ونفّذ منطق عمل (بما في ذلك الاختبارات المتعلقة به) يعالج تغيير الماحيات permissionFlags من خلال الواجهة البرمجية (انتبه لمعضلة الدجاجة والبيضة هنا: فكيف يمكن لشخص معين الحصول على تصريح لتغيير التصريحات؟) نفّذ اﻵن اﻷمر npm run test ومن المفترض أن تجري الاختبارات بنجاح ويكون الخرج منسقًا بالشكل التالي: Index Test ✓ should always pass users and auth endpoints ✓ should allow a POST to /users (76ms) ✓ should allow a POST to /auth ✓ should allow a GET from /users/:userId with an access token with a valid access token ✓ should allow a GET from /users ✓ should disallow a PATCH to /users/:userId ✓ should disallow a PUT to /users/:userId with an nonexistent ID ✓ should disallow a PUT to /users/:userId trying to change the permission flags ✓ should allow a PUT to /users/:userId/permissionFlags/2 for testing with a new permission level ✓ should allow a POST to /auth/refresh-token ✓ should allow a PUT to /users/:userId to change first and last names ✓ should allow a GET from /users/:userId and should have a new full name ✓ should allow a DELETE from /users/:userId 13 passing (231ms) وهكذا نكون قد أنجزنا أسلوبًا سريعًا في التحقق من عمل الواجهة البرمجية بالطريقة التي نتوقعها. تنقيح التطبيق من خلال الاختبارات يمكن للمطورين الذين يعانون من إخفاقات غير متوقعة لشيفرتهم تعزيز قدرة شيفرات التنقيح لكل من winstonو Node.js عند تنفيذ سلسلة الاختبارات. فمن السهل مثلًا التركيز على استعلامات Mongoose التي تُنفّذ باستخدام اﻷمر DEBUG=mquery npm run test (لاحظ عدم وجود البادئة export والعامل && في الوسط مما يجعل بيئة العمل تستمر لتنفيذ أوامر أخرى). من الممكن أيضًا عرض خرج جميع عمليات التنقيح باستخدام npm run test-debug ويساعدنا في ذلك طريقة اﻹعداد التي اتبعناها في الملف package.json وبهذا نكون قد بنينا واجهة برمجية REST قابلة للتوسع ومدعومة بقاعدة بيانات MongoDB وسلسلة من الاختبارات المناسبة، لكن هناك بعض النقص. اﻷمان في تطبيقات Express.js ينبغي دائمًا الاطلاع على توثيق ي عند العمل مع التطبيقات المبنية عليها وكحد أدنى ما يتعلق بأفضل ممارسات اﻷمان: دعم إعدادات TLS. إضافة أداة وسيطة للتحكم بمعدّل الطلبات إلى الواجهة البرمجية مثل express-limit-rate التأكد من أمان اعتمديات npm (من الممكن تنفيذ البرنامج باستخدام npm audit، أو استخدام snyk). استخدام المكتبة Helmet للمساعدة في حماية التطبيق من نقاط الضعف الشائعة وهذا ما سنضيفه مباشرة إلى تطبيقنا: npm i --save helmet ومن ثم ندرجه ضمن الملف app.ts ونضيف الاستدعاء ()app.use: import helmet from 'helmet'; // ... app.use(helmet()); ولا يعني استخدام Helmet أنك بأمان، فكل خطوة ولو كانت بسيطة لتأمين التطبيق لها فائدتها. احتواء الواجهة البرمجية ضمن Docker لم نخض في تفاصيل Docker كثيرًا في سلسلة مقالاتنا على الرغم من استخدامه لاحتواء قاعدة البيانات MongoDB. فإن كنت تريد التقدم خطوة أبعد في العمل مع Docker، أنشئ ملفًا بالاسم Dockerfile ودون لاحقة في جذر المشروع وضمّنه الشيفرة التالية: FROM node:14-slim RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY . . RUN npm install EXPOSE 3000 CMD ["node", "./dist/app.js"] تبدأ هذه اﻹعدادات بالسطر node:14-slim وهي النسخة الرسمية من Docker، ثم تشغّل الواجهة البرمجية ضمن الحاوية. يمكن أن تتغير هذه اﻹعدادات من حالة إلى أخرى، لكن هذه اﻹعدادات التي تبدو عامة مناسبة لمشروعنا. ولبناء هذه النسخة نفّذ الأوامر التالية في جذر المشروع مستبدلًا tag_your_image_here بما يناسب): docker build . -t tag_your_image_here ثم نفّذ ما يلي لتشغيل الواجهة ضمن الحاوية: docker run -p 3000:3000 tag_your_image_here يمكن اﻵن لقاعدة البيانات MongoDB و Node.js استخدام Docker، لكن علينا تشغيلهما بطريقتين مختلفتين. ونترك لك البحث عن كيفية إضافة تطبيق Node.js اﻷساسي إلى الملف docker-compose.yml كي يعمل التطبيق ككل من خلال أمر docker-compose واحد. خلاصة: استكشاف مهارات أخرى في بناء واجهات برمجية REST أجرينا في هذا المقال تحسينات واسعة على الواجهة البرمجية REST التي بدأنا العمل عليها في سلسلة مقالاتنا، إذ أضفنا طريقة للاستيثاق باستخدام مفاتيح JWT وبنينا نظام سماحيات مرن، وكتبنا مجموعة من الاختبارات المؤتمتة. يُعد ما فعلناه نقطة انطلاق قوية لمطوري الواجهة الخلفية الجدد والمتقدمين. ومع ذلك، قد لا يكون مشروعنا مثاليًا في بعض جوانبه وخاصة عند إنجاز نسخة إنتاج أو محاولة توسيعه. وإضافة إلى التمارين التي أو صينا بحلها خلال سلسلة المقالات السابقة، ما الذي يمكن تعلمه أيضًا؟ نوصي على مستوى بناء الواجهات البرمجية بالاطلاع على مواصفات بناء واجهات متوافقة مع OpenAPI. وننصح أيضًا المهتمين بتطوير المشاريع، تجربة إطار العمل NestJS المبني على أساس Express.js، فهو أكثر قوة وتجريدًا. لهذا، من الجيد العمل على مشروعنا كي تعتاد العمل مع أساسيات Express.js بداية. ولا ننسى بالطبع إطار العمل GraphQL الذي لا يقل أهمية عنه، إذ يلقى رواجًا وانتشارًا كبديل عن الواجهات البرمجية REST. أما فيما يخص موضوع الاستيثاق والسماحيات،فقد أنشأنا أسلوبًا يعتمد على رايات تُختبر بالصيغة الثنائية ومولد دوال وسيطة لتحديد تلك الرايات يدويًا. ولكي تشعر بثقة أكبر إن قررت زيادة حجم تطبيقاتك، يجدر بك النظر في مكتبة CASL التي تتكامل مع Mongoose. إذ تزيد هذه المكتبة مرونة النهج الذي اتبعناه ويسمح بتعريفات موجزة للإمكانات التي يجب أن يسمح بها تصريح معين مثل: can(['update', 'delete'], '(model name here)', { creator: 'me' }); بدلاً من الدوال الوسيطة المخصصة لهذا اﻷمر. كما قدمنا نقطة انطلاق في عملية الاختبار المؤتمت، ولكن بعض الموضوعات المهمة كانت خارج نطاقنا سلسلة مقالاتنا، لهذا نوصيك أن: تستكشف اختبار الوحدة Unit test كي تختبر كل وحدة برمجية على حدى. يمكن استخدام Mocha و Chai لهذا الغرض أيضًا. تلق نظرة على أدوات تغطية التعليمات البرمجية، والتي تساعد في تحديد الثغرات في مجموعة الاختبارات من خلال عرض أسطر الأوامر التي لا تُنفَّذ أثناء الاختبار. تُمكّنك هذه اﻷدوات من استكمال الاختبارات في تطبيقنا، بما يناسبك، لكنها قد لا تكشف عن كل الحالات التي تسبب المشاكل، مثل الحالة التي تدرس إمكانية تعديل المستخدم لسماحياته من خلال طلب PATCH إلى الوجهة users /:userId/. تجرب أساليب أخرى للاختبارات المؤتمتة. لقد استخدمنا مبدأ التطوير القائم على السلوك (BDD) من خلال الدالة ()expect من CHAI، لكن المكتبة تدعم أيضًا الدوال ()should و ()assert. ومن الجيد أيضًا تعلم مكتبات اختبار أخرى، مثل JEST. بصرف النظر عن هذه الموضوعات، فإن الواجهة البرمجية REST التي بنيناها اعتمادًا على Node.js/TypeScript قابلة للتوسع. فقد ترغب في في تنفيذ المزيد من البرامج الوسيطة لفرض منطق عمل مشترك لاستغلال مورد المستخدمين القياسي. لن نتعمق في ذلك هنا، لكنني سنكون سعداء بتقديم التوجيه والنصائح من خلال نقاش الصفحة. للاطلاع على المشروع بصيغته النهائية راجع المستودع المخصص على جيتهب أو حمله من هنا مباشرة.rest-series.rar ترجمة -وبتصرف للقسم الثاني من مقال Building a Node.js TypeScript REST API, Part 3 MongoDB, Authentication, and Automated Tests اقرأ أيضًا المقال السابق: بناء واجهة برمجية متوافقة مع REST في بيئة Node.js القسم الثالث: قاعدة البيانات MongoDB والاستيثاق مدخل إلى إطار عمل الويب Express وبيئة Node الاستيثاق عبر مفتاح المستخدم المشفر (token authentication) في تطبيق Node.js و React اعتبارات نشر مشاريع Node.js وExpress على الويب
  6. بخصوص تبديل الدورة، تستطيع ذلك من خلال التحدث لمركز المساعدة وسيتم مساعدتك، لكن تحدث إليهم من الحساب المُشترك بالدورة مع ذكر وقت الإشتراك وسبب تبديل الدورة.
  7. انا اشتركت مند مده قصيره بكورس php و لم ادرسه بعد و لكن اود تغييره بكورس python هل من الممكن ذلك؟
  8. إما أنك لم تقم بحفظ الكود ثم تحديث الصفحة لتظهر التحديثات، أو هناك مشكلة في الكود مثل عدم توافق أسماء الكلاسات مع هيكل HTML لديك. إليك كود متوافق مع التنسيقات التي لديك مع بعض التحسينات: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Enhanced NavBar</title> <style> header .navbar nav { display: flex; justify-content: space-between; align-items: center; max-width: 1100px; margin: auto; padding: 10px; background-color: #f8f9fa; border-bottom: 1px solid #ddd; } header .navbar nav ul { display: flex; justify-content: center; align-items: center; list-style: none; padding: 0; margin: 0; } header .navbar nav ul li { margin: 0 15px; } header .navbar nav ul li a { text-decoration: none; color: #333; font-weight: bold; } header .navbar .logo { font-size: 1.5em; font-weight: bold; color: #333; } header .navbar .search-bar { display: flex; align-items: center; } header .navbar .search-bar input[type="text"] { padding: 5px; border: 1px solid #ccc; border-radius: 3px; } header .navbar .search-bar button { padding: 5px 10px; border: none; background-color: #007bff; color: white; border-radius: 3px; margin-left: 5px; } </style> </head> <body> <header> <div class="navbar"> <nav> <div class="logo">MyLogo</div> <ul> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Services</a></li> <li><a href="#">Contact</a></li> </ul> <div class="search-bar"> <input type="text" placeholder="Search..."> <button type="button">Search</button> </div> </nav> </div> </header> </body> </html>
  9. نعم يمكن استخدام لغة البرمجة بايثون لأداء مثل هذه المهام المتعلقة بالتعديل على ملفات PDF، هناك مكتبات عدة في بايثون يمكن أن تساعدك مثل مكتبة PyMuPDF و reportlab.، سأحاول كتابة كود مختصر لكيفية استخدام بايثون لإزالة جزء من صفحة PDF وتلوينه بلون ثابت: import fitz # PyMuPDF def remove_and_colorize(input_pdf_path, output_pdf_path, rect, color): # فتح ملف الـ PDF doc = fitz.open(input_pdf_path) # تكرار على جميع الصفحات for page in doc: # إزالة المحتوى داخل المستطيل المحدد page.draw_rect(rect, color=color, fill=color) # حفظ التعديلات في ملف جديد doc.save(output_pdf_path) # مثال على إحداثيات المستطيل (يسار، أعلى، يمين، أسفل) بالـ points rect = fitz.Rect(100, 100, 300, 200) # قم بتعديل الإحداثيات حسب الحاجة color = (1, 1, 1) # اللون الأبيض (قيم RGB بين 0 و 1) input_pdf_path = "input.pdf" output_pdf_path = "output.pdf" remove_and_colorize(input_pdf_path, output_pdf_path, rect, color) في هذا المثال تقوم الوظيفة remove_and_colorize بفتح ملف PDF، ثم تقوم بتكرار جميع الصفحات وتلوين الجزء المحدد بالمستطيل باللون المحدد، بعدها يتم حفظ الملف المعدل في مسار جديد.
  10. السلام عليكم يا أهل الخبرة كنت بستفسر عن إمكانية البايثون للمساعدة في عملية بكررها كتير في شغلي وهي التعديل على صفحات PDF وإزالة جزء منها وتبديله بلون فقط كأنه ختم في المكان الثابت اللي بيتغير مرفق صورة غلاف كتاب للتوضيح مثال مش أكتر ونفس الصورة بعد التعديل مثلًا عايز أشيل جزء ثابت في كل الصور اللي عندي وألون الجزء ده بلون ثابت بردو ففيدونا أفادكم الله .. وجزاكم الله خيرًا غلاف.pdf غلاف بعد التعديل.bmp
  11. JDDJDJDJDJDJKCKCBCKLQFKQ FKKBFLKEFKHQFEKJLFEHJFEJQEFHJBECIJOKBJCHKJDVANKJKGJN C DV D DF FDV D GG
  12. البارحة
  13. يفضل إرسال صورة للتنسيق الذى يظهر لديك وأيضا شيفرة html وما هو الذى تريد تحقيقه حتى نرى أين المشكلة .
  14. header .navbar nav { display: flex; justify-content: space-between; align-items: center; max-width: 1100px; margin: auto; } header .navbar nav ul { display: flex; justify-content: center; align-items: center; } لماذا لا يتم تنفيذ هذا الكود, لا توجد نتيجة عن تحديث الصفحة
  15. ستحتاج إلى مكتبات بايثون المتخصصة في معالجة الصور ومطابقتها، وواحدة من المكتبات الشائعة والمناسبة للغرض هي مكتبة OpenCV ومكتبة scikit-image. بعد تثبيت المكتبات: pip install opencv-python pip install scikit-image عليك كتابة سكريبت يقوم بتحميل الصور من مجموعة البيانات dataset، واستخدام تقنيات مثل استخراج الميزات (feature extraction) لمقارنة الصور. للتوضيح بمثال بسيط حول كيفية مقارنة الصور باستخدام OpenCV: import cv2 import os import numpy as np from sklearn.metrics.pairwise import cosine_similarity def load_images_from_folder(folder): images = [] for filename in os.listdir(folder): img = cv2.imread(os.path.join(folder, filename)) if img is not None: images.append((filename, img)) return images def extract_features(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) sift = cv2.SIFT_create() keypoints, descriptors = sift.detectAndCompute(gray, None) return descriptors def find_similar_images(image_path, dataset_folder, top_n=5): input_image = cv2.imread(image_path) input_features = extract_features(input_image) dataset_images = load_images_from_folder(dataset_folder) similarities = [] for filename, dataset_image in dataset_images: dataset_features = extract_features(dataset_image) if dataset_features is not None and input_features is not None: similarity = cosine_similarity(input_features, dataset_features) similarities.append((filename, np.mean(similarity))) similarities.sort(key=lambda x: x[1], reverse=True) return similarities[:top_n] if __name__ == "__main__": dataset_folder = 'path/to/dataset' input_image_path = 'path/to/input/image.jpg' similar_images = find_similar_images(input_image_path, dataset_folder) print("Similar Images:") for filename, similarity in similar_images: print(f"Filename: {filename}, Similarity: {similarity}") بعد ذلك تأتي خطوة دمج بايثون مع PHP، مثلاً من خلال استخدام shell_exec لتنفيذ سكريبت بايثون وإرجاع النتائج أو من خلال API بايثون أو subprocess في PHP اختر ما تريد، وللتوضيح ساستخدم shell_exec كالتالي: <?php $input_image_path = 'path/to/input/image.jpg'; $output = shell_exec("python3 path/to/your/script.py $input_image_path"); echo "<pre>$output</pre>"; ?> بعد أن تحصل على قائمة الصور المشابهة من سكريبت بايثون، تستطيع عرض الصور باستخدام PHP، كالتالي: <?php $similar_images = json_decode($output, true); echo "<h1>Similar Images:</h1>"; foreach ($similar_images as $image) { echo "<div>"; echo "<img src='path/to/dataset/{$image['filename']}' alt='{$image['filename']}'>"; echo "<p>Similarity: {$image['similarity']}</p>"; echo "</div>"; } ?>
  16. في عام 2001، اجتمع ثلاثة أصدقاء في إدنبرغ Edinburgh ليتشاركوا اقتراحاتهم عن مشروع يعملون عليه معًا، وقد أعدّ كلٌّ منهم مجموعة أفكار ليصوتوا عليها ويختاروا الفكرة الأفضل، لكن واحدًا منهم فقط كان قد أعدَّ نموذجًا أوليًا لفكرته. شارك غاريث ويليامز Gareth Williams مع أصدقائه ملف إكسل كان قد أعده ليساعده على إيجاد رحلات طيران رخيصة، مما حمَّس الجميع مباشرةً لفكرته المتمثلة في إنشاء موقع ويب للبحث عن رحلات طيران؛ وفي عام 2002 صدرت النسخة الأولى من موقع سكاي سكانر Skyscanner لحجوزات الطيران. لا تقتصر أهمية إعداد النماذج الأولية prototypes على إقناع المستثمرين وشركاء المشروع بالفكرة، بل تستخدَم هذه النماذج أيضًا في مرحلة التطوير للمساعدة على التخطيط وتقييم المنتج واختباره قبل إنفاق الكثير من المال والوقت. لكن كيف يتم إعداد هذه النماذج؟ هذا ما سنتحدث عنه فيما يلي. ما هو النموذج الأولي ولماذا نحتاج إليه؟ في مجال تصميم البرمجيات، يُعَد النموذج الأولي عبارة عن تصميم أولي للمنتج يمكن أن يتفاعل معه المستخدمون. تكون النماذج الأولية غير مبرمجة بعد، لكن يمكن استعراضها من خلال أدوات معينة للتنقل بين الشاشات ومحاكاة تجربة المستخدم النهائي. تسمح النمذجة الأولية بتوفير الوقت والمال في مرحلة التطوير، لأنها تساعد على اكتشاف المشاكل التي تعيق سهولة الاستخدام في مرحلة مبكرة وتحسين التصميم قبل مرحلة بناء المنتج. والآن لماذا نحتاج إلى نماذج أولية؟ من أجل طرح الأفكار: تُعَد النماذج الأولية طريقةً سهلةً لتجربة الأفكار المختلفة والتعبير عنها ضمن فريق التصميم. من أجل جذب الأطراف المعنية: كما قال غاريث ويليامز مؤسس منصة سكاي سكانر: "إذا كان بوسعك أن تُري الآخرين بعينهم ما الذي تريد القيام به عندما تريد منهم المساعدة في ذلك، فسيكون موقفك أقوى بكثير مقارنةً بأن تحاول فعل ذلك من خلال الكتابة أو الشرح". من أجل التواصل بين فريق التصميم وفريق الهندسة: تساعد النماذج الأولية على تقليل الشكوك التي قد تنتاب المطورين عند تقديم التصميم إليهم. من أجل تحديد المشاكل التي تعيق سهولة الاستخدام: النموذج الأولي القابل للنقر عليه يسمح للمصممين بمراقبة تفاعلات المستخدم النهائي قبل كتابة أي سطر من الأكواد؛ أما بالنسبة للمستخدمين، فالنماذج الأولية تبدو لهم بنفس مظهر المنتج النهائي وبنفس انطباعه، لذا فإن تفاعلاتهم تكون طبيعية وأقرب ما يمكن إلى طريقة تفاعلهم في الواقع. هناك الكثير من الالتباس حول ماهية النموذج الأولي وتمييزه عن غيره من المفاهيم المستخدَمة لأغراض مشابهة، مثل النماذج المُحاكية mockups والرسوم التخطيطية wireframes ونموذج إثبات المفاهيم proof of concept. لنشرح الفروقات بين هذه المصطلحات فيما يلي: الفرق بين نموذج إثبات المفاهيم PoC والنموذج الأولي prototype ونموذج الحد الأدنى القابل للتطبيق MVP نموذج إثبات المفاهيم PoC هو التمثيل الأبسط والأقل تكلفةًً الذي يمكنك إنشاؤه للمنتج، والغرض منه هو التحقق من الجدوى التقنية لفكرة المنتج. يمكن أن يكون نموذج إثبات المفاهيم على شكل عرض تقديمي أو مقطع فيديو أو رسم بياني أو شكل أولي للوظيفة، أو أي شيء يوضح لصنّاع القرار إمكانية وضرورة تنفيذ ذلك، مع توضيح النطاق والتقنيات اللازمة للمشروع في معظم الأحيان. نموذج الحد الأدنى القابل للتطبيق MVP يمثل قفزةً نوعيةً قياسًا بنموذج إثبات المفاهيم، فهو يُعَد نسخةً مبكرةً من المنتج ذات ميزات أصغرية، ويهدف إلى معرفة كيفية قبول الجمهور للمنتج، وغالبًا ما يجري إصداره للجمهور من أجل الاختبار وجمع الآراء. النموذج الأولي prototype يُعَد مرحلةً وسطيةً بين النموذجين السابقين، وهو عملي أكثر من نموذج إثبات المفاهيم PoC كما أنه أكثر تفصيلًا منه، لكنه يركز فقط على التصميم دون أن يحتوي فعليًا على أية ميزة، خلافًا لنموذج الحد الأدنى القابل للتطبيق MVP. لمحة عامة توضح الفرق بين نموذج إثبات المفاهيم PoC والنموذج الأولي prototype ونموذج الحد الأدنى القابل للتطبيق MVP. الفرق بين الرسم التخطيطي Wireframe والنموذج المحاكي Mockup والنموذج الأولي prototype الرسوم التخطيطية والنماذج المحاكية هي عبارة عن إصدارات مختلفة للنماذج الأولية. توضيح الفرق بين الرسوم التخطيطية والنماذج المحاكية والنماذج الأولية عالية الدقة. الرسوم التخطيطية Wireframes هي الخطوط العريضة للتصميم، وهي توضح مكان المحتوى والأزرار والنص وما إلى ذلك. ويمكن إنشاء الرسم التخطيطي يدويًا أو باستخدام أدوات خاصة. تمثل الرسوم التخطيطية الهيكل الأساسي لواجهة المستخدم دون الخوض في التفاصيل، وهي بذلك تسمح للمصممين بتعديل العناصر ومواضعها بسرعة. ويُعَد الرسم التخطيطي هو الخطوة الأولى في عملية التصميم بعد جمع المعلومات، ويعمل على إنشائها عادةً فريق تجربة المستخدم. النماذج المحاكية Mockups هي عبارة عن إصدارات متقدمة للرسوم تخطيطية تم ملؤها بالمحتوى من شعارات وصور ونص حقيقي، ينشؤها مصممو واجهة المستخدم، ويكونون على معرفة مسبقة بالألوان والمسميات والخطوط التي من المرجح اعتمادها في المنتج النهائي. في الأخير، الشكل النهائي للنماذج الأولية هو النموذج الأولي الوظيفي القابل للعمل عليه، وهو إصدار قابل للنقر من النموذج المحاكي، قادر على محاكاة تجربة المستخدم الحقيقية إلى حدٍّ بعيد، وله أنواع مختلفة أيضًا: نموذج أولي للجدوى: يستخدم لمعرفة أفضل تقنية تناسب فكرة المنتَج من أجل تجنب المجازفة. نموذج أولي عالي الدقة hi-fi: يستخدم لجمع بيانات أداء المستخدم (مستوى الرضا، عدد المشاكل في كل جلسة، إلخ). نموذج أولي ببيانات حية: عندما تملأ النموذج الأولي ببيانات حقيقية بدلًا من بيانات العينة لجعل بيئة الاختبار صعبة التنبؤ بها. ربما تشعر الآن أن تمييز هذه الأنواع عن بعضها بات أكثر صعوبة، لكن لا بأس، فكل مؤسسة تختار المفاهيم التي تناسبها ولا تلتزم كثيرًا بالمسميات. ولحسن الحظ، مهما كان نوع النموذج الأولي الذي تريد تصميمه، يمكنك تطبيق أي من المناهج التالية لإنشائه. مناهج إعداد النماذج الأولية كما هو الحال في إنشاء البرمجيات، هناك طرائق مختلفة لإنشاء النماذج الأولية. نبين فيما يلي بعض هذه الطرائق ومتى يمكن استخدام كل منها. النمذجة الأولية سريعة الإقصاء كما يشير الاسم، يكون النموذج الأولي هنا صالحًا للاستخدام لفترة قصيرة، مثلًا لدورة تطوير واحدة فقط، وخلال هذه الفترة يخضع هذا النموذج لبضعة دورات من التعديل والاختبار، وفي النهاية يُحال إلى التخلص منه. على سبيل المثال، النماذج الأولية الورقية تُعَد من النمط سريع الإقصاء، فبعد أن يقبل بها جميع الأطراف، يحين وقت بناء نموذج أولي جديد. تساعد النماذج الأولية سريعة الإقصاء على جمع ملاحظات المستخدمين المبدئيين، التي تستخدم لاحقًا لإنشاء نموذج أولي وظيفي. النمذجة الأولية التطورية هذه هي المنهجية الأقرب للوصول إلى النموذج الأولي المطلوب، وتتضمن إنشاء نموذج أولي ثم تقديمه إلى المستخدمين وجمع ملاحظاتهم، ثم إدخال تحسينات على النموذج بناءً على تلك الملاحظات وعرضه على المستخدمين مرةً أخرى، وتكرار ذلك عدة مرات إلى حين الوصول إلى نموذج أولي يحظى بالقبول التام للمستخدمين. بهذه الطريقة يتطور النموذج الأولي وصولًا إلى نسخته النهائية. تضمن هذه المنهجية أن يلبي المنتج النهائي متطلبات المستخدم باحتمال كبير، لأن المستخدمين يشاركون في عملية إنشائه بطريقة فعالة. النمذجة الأولية التجميعية تُستخدم هذه المنهجية عادةً لمنتجات المؤسسات الكبيرة، وتتضمن تجزئة المنتج إلى عدة أجزاء وإنشاء نموذج أولي لكل جزء على حدة. يجري اختبار كل نموذج أولي بشكل منفصل ويتم تحسينه قبل دمج النماذج الأولية الجزئية معًا لتشكيل النموذج الأولي المتكامل. ولضمان ملاءمة جميع الأجزاء لبعضها من ناحية المظهر والانطباع، يجب إعداد دليل إرشادي يوضح الأسلوب المتبع مسبقًا. تتميز هذه المنهجية بتوفير الوقت اللازم لإعداد النماذج الأولية، لأنها تسمح بالعمل على أجزاء مختلفة من المنظومة بالتزامن. النمذجة الأولية الفائقة تُستخدم هذه المنهجية تحديدًا في تطوير الويب، وتُقسَم إلى ثلاث مراحل وفقًا للطبقات الثلاثة لتطبيق الويب: بناء رسوم تخطيطية ثابتة بلغة HTML من أجل طبقة العرض. تحويل الرسوم التخطيطية بلغة HTML إلى نموذج أولي وظيفي من خلال ربطها بخدمات محاكية. تطبيق الخدمات على النسخة النهائية من النموذج الأولي. تسمح النمذجة الأولية الفائقة بعرض النموذج الأولي على المستخدمين في كل مستوى. لا تنشغل كثيرًا بالتصنيف، بل استغل هذه المنهجيات لصالحك. سنتحدث فيما يلي عن الخطوات الرئيسية لإعداد النماذج الأولية، بغض النظر عن المنهجية المتبعة. مراحل إعداد النماذج الأولية يُعَد إنشاء النماذج الأولية عملًا تصميميًا، لذا يمكن التعامل معه مثل أي مشروع تصميم آخر، وذلك ابتداءً من جمع المعلومات حول ما يجب أن يتضمنه التصميم، وصولًا إلى اختبار مدى جودة تلبية التصميم النهائي لمتطلبات المستخدم. لنوضح المزيد من التفاصيل والنصائح فيما يلي. مراحل إنشاء النماذج الأولية. المرحلة الأولى: جمع المتطلبات في مجال تطوير الويب، تتمثل المتطلبات بالأهداف والاحتياجات التي يجب أن يلبيها المنتج، وتُقسم هذه المتطلبات إلى متطلبات وظيفية (ميزات المنتج) ومتطلبات غير وظيفية (أداء المنظومة). عادةً ما يُبنى النموذج الأولي على أساس المتطلبات الوظيفية، والتي تُجمَع بطرائق مختلفة: المقابلات: وتتضمن اجتماع محلل البيانات (وهو الشخص المسؤول مباشرةً عن توثيق المتطلبات) مع واحد أو أكثر من الأطراف المعنية والمستخدمين النهائيين من أجل إجراء مقابلة. قد يطرح محلل البيانات أسئلةً حول عمل الشخص الذي يُجري معه مقابلة، ومهامه، والمشكلات الشائعة التي يواجهها؛ كما قد يسأله عن كيفية تخطيه لهذه المشكلات حاليًا، وما الأدوات التي يستخدمها وكيف يستخدمها. غالبًا ما يجري تسجيل المقابلات وتفريغ محتواها كتابيًا، لكي يتسنى لبقية أعضاء الفريق معرفة كافة التفاصيل دون تفويت أيٍّ منها. قد تكون المقابلة الجماعية خيارًا جيدًا، نظرًا لأن الأسئلة المطروحة قد تثير المزيد من النقاشات، ويجب أن يدوِّن محلل البيانات المشكلات التي يتفق الجميع عليها. الاستبيانات: تُستخدم الاستقصاءات والاستبيانات عندما يكون المستخدمون والأطراف المعنية مقيمون في مناطق متباعدة جغرافيًا، مع الحاجة للوصول إلى العشرات أو المئات منهم في عملية مسح واحدة. ويتطلب إنشاء استبيان جيد بحد ذاته مهارةً كبيرة، كما يجب أن يكون الاستبيان مختصرًا قدر الإمكان، مع ضمان القدرة على تحليل نتائجه. يمكنك البدء بتحديد القرارات التي ستتخذها بناءً على الإجابات التي ستحصل عليها، بعدها حدد عدد الإجابات التي ستحتاج إليها لاتخاذ القرار، كأن تقرر أنه إذا أبدى 15% على الأقل من المشاركين بالاستبيان اهتمامهم بميزة معينة، فسوف تضيف هذه الميزة إلى وثيقة المتطلبات. ملاحظات المستخدم: كما هو الحال في المقابلات، يجتمع محلل البيانات مع أحد المستخدمين في جلسة، لكنه في هذه المرة يتولى مراقبة أداء المستخدم لمهمة ما، حيث يُطلَب من المستخدم أن يؤدي المهمة بالطريقة التي ينفذها بها عادةً، ويسجل المحلل كل حدث بدقة، وفي بعض الأحيان يحدد عمليات معينة. يمكن في عملية جمع ملاحظات المستخدم أن تستعمَل تسجيلات الشاشة والفيديو أيضًا. مع ذلك، يمكن أن يتصرف المستخدم بطريقة غير طبيعية تحت المراقبة، وغالبًا ما يتطلب الأمر العديد من الجلسات من أجل التقاط جميع التفاصيل. بعد جمع المعلومات من المستخدمين والأطراف المعنية، يراجع الأعضاء المسؤولون جميع التسجيلات ويختارون المتطلبات الأكثر تكرارًا، وتمثل هذه المرحلة انطلاقةً ممتازةً لإعداد النموذج الأولي. المرحلة الثانية: إعداد تصميم سريع تتضمن هذه المرحلة إنشاء أبسط نسخة من النموذج الأولي (الرسوم التخطيطية والنماذج المحاكية)، وهناك عدة طرائق لتصميم واجهة المستخدم UI: الرسم: يمكنك الرسم على دفتر أو على لوح أبيض موجود في المكتب أو رقميًا، وذلك باستخدام أدوات رسم أو خرائط ذهنية على الإنترنت. يُعَد الرسم أسرع طريقة لتمثيل أفكار واجهة المستخدم، وهو تمثيل مؤقت لتحسين الأفكار، لكنه ليس أفضل طريقة للتصميم، فهناك طرائق أكثر فعالية، ومع ذلك يبقى الرسم أنسب طريقة لتبادل الأفكار بسرعة. الأدوات المستخدمة: Miro، Diagrams.net، Sketch.io، أدوات الرسم المادية. النماذج الورقية: وهي طريقة قديمة ولكنها مفضلة لدى المصممين، فالنماذج الأولية الورقية رخيصة الثمن ويمكن لأي شخص صنعها، كما أنها نشاط مثالي عندما يتعاون الفريق على إنجازه. تأخذ النماذج الأولية الورقية عادةً شكل رسوم تخطيطية، وتتضمن استخدام الطي والتمرير بالإضافة إلى تعدد الطبقات. تستغرق النماذج الورقية المعقدة وقتًا لإنجازها، ومع ذلك فالنتائج تكون تقريبية، مما يُشعِر المستخدمين بأريحية البوح بانتقاداتهم. الأدوات المستخدمة: printable prototype templates phone stencils أدوات الرسم المادية. مقطع يوتيوب توضيحي: أمثلة على تمرير العناصر في نموذج أولي ورقي. من الأفضل تأجيل كل أشكال التصميم الجاد إلى المرحلة التالية. المرحلة الثالثة: تطوير النموذج الأولي استنادًا إلى المتطلبات المسجلة والرسوم التخطيطية التقريبية، يبدأ تصميم نموذج أولي فعلي، والذي قد يتخذ أشكالًا عديدةً كما ذكرنا سابقًا، لكن الغاية تبقى واحدة وهي إنشاء منتج تفاعلي عالي الدقة مصمم بجودة عالية، لذا من الممكن تخطي بعض أشكال النماذج الأولية إذا لم تكن ضرورية. على أية حال، لنتعرف لماذا ومتى نستخدم كل شكل من أشكال النماذج الأولية. الرسوم التخطيطية القابلة للنقر: يتم إنشاؤها من خلال برمجيات الرسم التخطيطي، ويمكن استخدامها في اختبارات المحاكاة وسهولة الاستخدام. يمكن رفع الرسومات والنماذج الورقية التي أُعِدَّت مسبقًا إلى برنامج الرسم التخطيطي من أجل ربط الشاشات وإنشاء جميع العناصر رقميًا لتبدو بمظهر أفضل. ورغم أن إنشاء هذا النوع من النماذج الأولية سهل وسريع الإعداد، إلا أنه قد يتطلب الشرح والتوضيح، فقد لا يدرك المستخدمون أن هذا ليس هو الشكل النهائي. الأدوات المستخدمة: مايكروسوفت باوربوينت، Balsamiq و Figma و Justinmind و Lucidchart و Mockflow وغيرها. متى تُستخدَم؟ تستخدم الرسوم التخطيطية القابلة للنقر في المراحل المبكرة، عندما يتطلب الأمر اختبار إصدارات مختلفة من التصميم قبل اعتماد أحدها. النماذج المحاكية Mockups: وهي عبارة عن صور ثابتة لكن أكثر واقعية للمنتج النهائي، تسمح بتطبيق متطلبات الأطراف المعنية فيما يتعلق بالعلامة التجارية والألوان والخطوط والأيقونات والعناصر الأخرى. لا تحظى النماذج المحاكية بشعبية كبيرة، لأن الكثير من المصممين يقررون عدم إضاعة الوقت عليها والانتقال مباشرةً إلى إنشاء نماذج أولية عالية الدقة على أساس الرسوم التخطيطية، ومع ذلك فإن النماذج المحاكية مهمة جدًا لدراسة الانطباع العاطفي واختبار العناصر المرئية المهمة. الأدوات المستخدمة: InVision، Sketch، Figma، Adobe XD، Framer. متى تستخدَم؟ تستخدم النماذج المحاكية في التسويق والعروض التقديمية لربط الأطراف المعنية بالمنتج عاطفيًا قبل إنشاء النموذج الأولي. إنشاء نموذج مُحاكٍ النماذج الأولية عالية الدقة: وهي التمثيل الأقرب للمنتج النهائي، وتسمح باختبار كيفية تفاعل الناس مع كل من العناصر المرئية للمنتج ومسار المستخدم، كما أنها الطريقة الوحيدة لاختبار الرسوم المتحركة والانتقالات والتفاعلات الصغيرة. بالإضافة إلى ذلك، فإن النموذج الأولي عالي الدقة يعطي الفريق فكرةً جيدةً عن الوقت والجهد اللازمين للتطبيق الفعلي للمنتج، لكنه من ناحية أخرى أصعب إنشاءً ويتطلب وقتًا أطول، كما أن إجراء إصلاحات عليه يستغرق وقتًا أطول مقارنةً بالرسوم التخطيطية. بالنسبة لإمكانية إجراء محاكاة في هذا النموذج الأولي فهي عالية جدًا، وذلك باستخدام بعض الأدوات التي تسمح بالوصول إلى بيانات حية أو حتى برمجة بسيطة. الأدوات المستخدمة: Proto.io، Figma، Flinto، Framer، Principle، Invision. متى تُستخدَم؟ تمثل النمذجة الأولية عالية الدقة المرحلة الأخيرة من التصميم، وتُجرى مباشرةً قبل الشروع بمرحلة التطوير من أجل تقييم سهولة استخدام المنتج، كما أنها وسيلة مساعدة لا يمكن الاستغناء عنها عند تقديم العروض والشروحات. يُنصح بعدم البدء بهندسة المنتج قبل إتمام إعداد النموذج الأولي والموافقة عليه، مما يساعد على تجنب الكثير من المشكلات اللاحقة غير المتوقعة. مقطع يوتيوب توضيحي: مثال عن نموذج أولي تفاعلي عالي الدقة المرحلة الرابعة: اختبار النموذج الأولي وتحسينه سواءً قررت الاكتفاء برسوم تخطيطية أو إعداد نموذج أولي نهائي، فإن الخطوة التالية هي عرض النموذج الأولي على الأطراف المعنية والمستخدمين لأخذ آرائهم. ومن أجل تحقيق ذلك، تحتاج إلى التواصل معهم عبر الإنترنت أو على أرض الواقع ليستخدموا النموذج الأولي. عند تقديم النموذج الأولي إلى المستخدم، يُطلب منه إنجاز مهمة عبره دون إعطائه الكثير من التعليمات حول كيفية أداء ذلك، ويجري تسجيل جميع الإجراءات والنقرات وحتى أحيانًا تتبُّع العينين، مع إيلاء اهتمام خاص بأي مشكلات أو حالات خروج عن المسار المتوقع للمستخدم. يُطلب عادةً من المستخدم أن يتحدث بصوت مسموع عن أي أفكار أو تعليقات أو التباسات يواجهها أثناء الاستخدام. يمكن إجراء ذلك وجهًا لوجه أو عبر مكالمة فيديو أو حتى بدون التواصل المباشر مع المستخدم. تتيح لك منصات اختبار المستخدم مثل Maze أو Lookback أو UserTesting إمكانية نشر نموذجك الأولي عبر الإنترنت، لتعمل هذه الأدوات على جمع البيانات اللازمة عن النقرات ومسارات المستخدم والأنماط. تقرير اختبار المستخدم موضحًا الأداء الوظيفي. كما هو الحال في المقابلات، توجَّه نتائج جلسات تقييم المستخدمين إلى جميع الأشخاص المسؤولين عن التحليل، ثم يجتمع الفريق ليقرر ما إذا كان النموذج الأولي بحاجة إلى المزيد من التحسينات (وعادةً يكون بحاجة لذلك)، ثم يبدأ بالتخطيط لمهمات إعادة التصميم، وتتكرر الدورة. الشركة وحدها هي من تقرر أن التحسينات أصبحت كافية وأن النموذج الأولي جاهز، لأن التحسينات والتغييرات لا تنتهي بطبيعة الحال. المرحلة الخامسة: تنفيذ المنتج يُعرَض النموذج الأولي المتفق عليه على فريق الهندسة لتبدأ بذلك عملية التطوير الطويلة والمعقدة، لكن دور فريق تجربة المستخدم UX وواجهة المستخدم UI لا ينتهي هنا، فوفقًا لمنهجية أجايل للتطوير، يجب اختبار كل جزء من المنظومة دوريًا وتكراره من أجل التحقق من سهولة استخدام المنتج الوظيفي الحقيقي، وهذا الاختبار يأتي بعد مقابلات وممارسات تقييم شبيهة بتلك التي تجرى أثناء النمذجة الأولية. بعد إطلاق المنتج، تستمر مراقبة سهولة الاستخدام واختبارها دوريًا، خصوصًا قبل طرح ميزات جديدة. وفي حال اتخاذ قرار بإجراء إعادة تصميم بعد بضع سنوات، فقد يتطلب ذلك إنشاء نموذج أولي مرةً أخرى. الخلاصة النموذج الأولي هو القناة التي تمر عبرها الفكرة، فليس هناك وسيلة للتعبير للآخرين عن المنتج الذي في مخيلتك أفضل من بناء نموذج يحاكيه. من ناحية أخرى فإن النموذج الأولي يُعَد دليلًا تأكيديًا على أنك لا تتحدث عن عبث بل تعرف فكرتك جيدًا، نظرًا لأنك قد أعددتَ نموذجًا تمهيديًا يمثل بشكل تقريبي ما سيبدو عليه الشكل النهائي. يُعَد إنشاء نموذج أولي أمرًا اعتياديًا وضروريًا في عمليات تصميم المنتج وتطويره، وهو من أهم الأشياء التي عليك فعلها إذا قررت طلب تمويل من جهةٍ ما لمشروعك، إذ يتطلب ذلك عرض نموذج أولي توضيحي يمثل فكرة منتجك بشفافية ووضوح، وهو أمر مهم من أجل كسب دعم طويل الأمد من الداعمين وتعزيز الثقة لديهم. باختصار، النموذج الأولي هو المفتاح لترسيخ ثقتك بفكرتك وكسب ثقة الفريق والأطراف المعنية والمورّدين، فضلًا عن بناء منتجات رائعة دون مجازفات أو مخاطر. ترجمة -وبتصرّف- للمقال Prototyping, Explained: Why and How to Build a Sample Version of a Product. اقرأ أيضًا المرحلة الرابعة في عملية التفكير التصميمي: مرحلة بناء النماذج الأولية الفوائد العملية لبناء النماذج الأولية تصميم النماذج الأولية للواجهات الرسومية: نصائح للتصاميم المتجاوبة والموجهة للأجهزة المحمولة
  17. للتحقق من المستخدم، هناك عدة استراتيجيات، منها JWT عبارة عن معيار مفتوح يستخدم لنقل البيانات بين الأطراف ككائن JSON مضغوط وآمن، بحيث تستخدم JWT لتأمين API ةيحصل المستخدم على رمز JWT بعد تسجيل الدخول، ويجب عليه إرسال هذا الرمز مع كل طلب لاحق. بعد ذلك يوجد الكوكيز هي ملفات صغيرة تُخزن على جهاز المستخدم وتُستخدم لتخزين معلومات الجلسة، وعند تسجيل الدخول، يمكن تخزين معرف الجلسة في الكوكيز، ويتم إرسال الكوكيز مع كل طلب إلى الخادم للتحقق من الجلسة. ويتوفر UUID هو معرف فريد يمكن استخدامه لتعريف المستخدمين بشكل آمن وفريد، وتُخزن UUID في قاعدة البيانات ويُستخدم لتعريف المستخدمين في التطبيقات. أما CSRF هو نوع من الهجمات التي تُجبر المستخدم على تنفيذ إجراءات غير مرغوب فيها على موقع ويب آخر يتمتع فيه المستخدم بالتصديق، والإعتماد على CSRF tokens هو آلية شائعة لمنع تلك الهجمات. وDevise هو حل كامل لمصادقة المستخدمين في تطبيقات Rails، بحيث يوفر Devise العديد من الميزات الجاهزة مثل التسجيل، تسجيل الدخول، استعادة كلمة المرور، تأكيد الحساب، وغيرها، تستطيع تثبيته كالتالي: إضافته إلى ملف gem: gem 'devise' ثم تثبيت Devise: rails generate devise:install بعد التثبيت، ستحتاج إلى اتباع التعليمات التي تظهر على الشاشة لتكوين Devise وإعداد البريد الإلكتروني لإرسال روابط تأكيد الحساب واستعادة كلمة المرور. وفي حال تقوم بإنشاء تطبيق جديد، فتستطيع إنشاء نموذج المستخدم باستخدام Devise: rails generate devise User ثم ترحيل قاعدة البيانات: rails db:migrate بعد ذلك إضافة مصادقة إلى عناصر التحكم من خلال before_action لحماية مساراتك: class ApplicationController < ActionController::Base before_action :authenticate_user! end و Rails يوفر حماية مدمجة ضد هجمات CSRF، لأنه يتم تضمين CSRF tokens تلقائيًا في النماذج والطلبات AJAX. ولو تحتاج استخدام JWT مع Devise، فهناك gem مثل devise-jwt ستوفر لك JWT مع Devise.
  18. فهمت حضرتك افتكرت ان درستها في الاحصاء شكرااا جدا لحضرتك
  19. يسعدنا الإعلان عن نشر كتاب جديد في أكاديمية حسوب بعنوان "دليلك إلى Node.js" الذي يشرح بأسلوب سهل ومبسط أساسيات التعامل مع بيئة التشغيل Node.js المخصصة لتنفيذ شيفرات جافاسكريبت JavaScript دون الحاجة إلى متصفح ويب. عند الانتهاء من قراءة هذا الكتاب ستكون قد اكتسبت كافة المعلومات الأساسية التي تحتاجها للبدء بالتعامل مع هذه البيئة، وستكون جاهزًا للبدء بتطوير مشاريعك البرمجية بلغة جافا سكريبت وتشغيلها على جانب العميل أو الخادم بكل مرونة. نرجو لك قراءة ممتعة!
  20. الانحراف المعياري (Standard Deviation) هو مقياس إحصائي يعبر عن مدى تشتت البيانات حول متوسطها، أي يوضح مدى انتشار القيم في مجموعة البيانات وما إذا كانت قريبة أو بعيدة عن المتوسط، إذا كان الانحراف المعياري صغيرا، فهذا يعني أن القيم قريبة من المتوسط وقليلة التشتت. أما إذا كان كبيرا، فهذا يعني أن القيم موزعة بعيدا عن المتوسط وتظهر تشتتا كبيرا.
  21. شكراا لحضرتك بس اي المقصود بالانحرف المعياري std
  22. لا يمكنك نسخ الأكواد من الفيديو مباشرة باستخدام "Ctrl + C" في حين يمكنك استخدام برامج التعرف الضوئي على الحروف (OCR) لاستخراج النص من لقطة شاشة للفيديو لكن بشكل عام لا أنصحك بذلك في حالة ما كنت في بداية مسارك التعلمي في البرمجة، فأفضل حلّ هنا هو كتابة الأكواد البرمجية بيديك لترسيخ المفاهيم وطريقة الكتابة فتلك الشيفرات ماهي إلا قواعد كتابة هي الأخرى نستخدمها في البرمجة. فلا تكتف بالنظر للأكواد ومعرفة الغرض منها بل طبّقي كل ما تتعلمينه وتعلمي كيفية كتابة وتنفيذ الشيفرات البرمجية ضمن محررات الشيفرات المخصصة للغة البرمجة التي اخترتها وجربي تعديل الكود وراقبي التغير في النتائج وابدئي بتنفيذ مشاريع بسيطة لتعززي الأسس والمفاهيم النظرية التي تعلمتها ومع الاستمرارية ستتشعين على مواصلة التقدم.
  23. في بدايات تعلمك، لا أنصحك بنسخ الكود لتوفير الوقت والمجهود، بل ما أنتِ بحاجة إليه هو العكس، فكتابة الكود بيديكِ هو تمرين مطلوب للتعود على الطريقة الصحيحة ومعرفة الفرق بين كتابة بعض الأمور والتي لن تلاحظيها في حال قمتي بنسخ الكود. الأمر أشبه بتعلم الكتابة لكن نتعلم البرمجة في تلك الحالة.
  24. إذا كنت لاترغب بال cdn يمكنك تنزيل المكتبة باستخدام npm , انتقل الى مجلد المشروع الخا بك و قم بتنفييذ التعليمة التالية في ال terminal npm install filepond filepond-plugin-file-validate-size filepond-plugin-file-validate-type هذه التعليمة ستقوم بتحميل المكتبة لديك بالاضافة الى 2 plugin اضافية هي file size and file type validation و الأن لديك طريقتتين لتضمين المكتبة في مشروعك : Include in a Blade View :: قم بفتح ملف ال blade view و قم باضافة السطرين التاليين في أعلى الملف <link href="{{ asset('css/filepond.min.css') }}" rel="stylesheet"> <script src="{{ asset('js/filepond.min.js') }}"></script> Include in Laravel Mix : اذا كنت تستخدم Laravel Mix قم باضافة الأسطر التالية لملف webpack.mix.js mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css') .copy('node_modules/filepond/dist/filepond.min.js', 'public/js') .copy('node_modules/filepond/dist/filepond.min.css', 'public/css'); ثم قم بتنفيذ التعليمة التالية في ال terminal npm run dev واذا كنت ترغب بال cdn فقط قم باضافة السطرين التاليين لملف ال blade <link href="https://unpkg.com/filepond@7.1.1/dist/filepond.min.css" rel="stylesheet"> <script src="https://unpkg.com/filepond@7.1.1/dist/filepond.min.js"></script> و يمكنك الرجوع للتوثيق الخاص بالمكتبة لرؤية طريقة استخدام المكتبة و امثلة عنها و هذا رابط التوثيق
  25. من الأفضل أولا فهم طريقة عمل ال api في لارافيل أو فقط الأساسيات اللازمة لإنشاء ال apis حتى تتمكن من إستكمال مشروعك . يمكنك إستخدام JTW في لارافيل أو Laravel Passport لتوفير طبقات الحماية والتفويض والمصادقة من خلال لارافيل . يمكنك قراءة هذا المقال عن Laravel Passport أو يمكنك البحث على يوتيوب عن شرح له . ويمكنك قراءة تلك الأجوبة حتى تتضح الرؤية لك أكثر عن كيفية الربط بين لارافيل Api و أى إطار عمل خاص بالواجهة الأمامية . ويمكنك إرسال ال token عن طريق ال session حيث يجب وضعه فى ال session وليس localStorage كما هنا . او يمكنك إضافته فى localStorage ولكن سيتوجب عليك في كل مرة ترسل طلب إلى الخادم أن تقوم بقراءته من localStorage وثم إرساله إلى لارافيل
  26. إن المكتبات الخاصة بالجافاسكريبت لا يمكن تثبيتها في لارافيل عن طريق composer ولكن يمكنك تثبيتها عن طريق npm في لارافيل إذا كانت متاحه في npm ويمكنك إستخدام vite أو laravel mix لتحزيم تلك المكتبات ومن ثم إستدعاء الملف المحزم في blade في لارافيل . ولكن بما أن filepond لا تحتاج لذلك فيمكنك تحميل الملف من cdn وثم وضعه في ملجد public وإستدعاءه عن طريق دالة asset في ملف blade في لارافيل هكذا . <script src="{{ asset('js/filepond.min.js') }}"></script> حيث هنا إفترضنا أن الملف بداخل مجلد js بداخل مجلد public في لارافيل .
  1. عرض المزيد
×
×
  • أضف...