محمد طاهر الموسوي

المساهمون
  • المساهمات

    230
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • Days Won

    21

السُّمعة بالموقع

109 Excellent

المعلومات الشخصية

  • النبذة الشخصية كيميائي ومصمم ومترجم عراقيّ، أهوى الخطّ العربي، وأعشق كذلك البرمجة وأحاول دائمًا تطوير مهاراتي فيها.

9 متابعين

آخر الزُوّار

3,095 زيارة للملف الشّخصي
  1. ﻻ بد أن تمرّ الشركة الناشئة - مهما كانت طبيعة فريق العمل - بمرحلة الوقوع في مشاكل لا تستطيع الخروج منها. والتحدّي الحقيقي - والحال هذه - هو تحديد الوقت اللازم لحلّ المشاكل التي تواجه الشركة، فهل ينبغي متابعة المشكلة والعمل على حلّها بشكل كامل مهما استغرق ذلك من وقت، أم يجدر طلب المساعدة عند العجز عن إيجاد الحلّ المناسب؟ تساعد قاعدة الـ 15 دقيقة كل فرد من أعضاء فريقك على إدراك القيمة الحقيقية لمفهوم الاكتفاء الذاتي، وتلزمهم في الوقت عينه بأن يلجأوا إلى أطواق النجاة عندما تقتضي الحاجة ذلك. تعلّمت هذه القاعدة البسيطة من Jeff مدير فريق دعم العملاء في شركتنا عندما كنت مهندسًا في هذا القسم. طلب مني Jeff في حال واجهتني مشكلة مع أحد العملاء أن أبحث بنفسي عن حلّ لها لمدة 15 دقيقة، وإن عجزت عن إيجاد الحلّ خلال هذه الفترة أتوجّه حينئذٍ إليه طلبًا للمساعدة. قاعدة الـ 15 دقيقة حاول أن تجد بنفسك حلًّا للمشكلة التي تواجهك ولمدة 15 دقيقة، وإن عجزت بعد هذه الفترة عن إيجاد الحل توجّه إلى شخص آخر طلبًا للمساعدة. يجب عليك إيجاد الحل بنفسك لم تكن غاية Jeff من هذه القاعدة الامتناع عن مساعدتي، بل العكس تماماً، فلو رميت جميع المشاكل التي تواجهني على اﻵخرين دون فهم أسبابها، لن أكون قادرًا حينها على حلّ أي مشكلة بمفردي. تعلّمك الدقائق الخمس عشرة مهارة مهمّة في مجال دعم العملاء، ألا وهي معرفة ما يجب عليك البحث عنه وكيفية إجراء البحث بصورة صحيحة. يجب على كلّ موظف في قسم دعم العملاء أو الفريق الهندسي أن يكون قادرًا على التنقل ضمن الأكواد الأساسية والتوثيقات، ويجب أن يدرك بأنّ الحلّ موجود لو أنّه أخذ الوقت الكافي للبحث عنه. إضافة إلى ما سبق، فإنّ هذه الدقائق الخمس عشرة ستجعلك على اطلاع جيّد بحيثيات المشكلة، ذلك لأنّك قد بحثت خلال هذه الفترة في الأكواد الأساسية، واطلعت على المحادثات السابقة، وفتّشت في محادثات Slack، وتفحّصت التوثيقات الداخلية، ورغم ذلك كله لم تفلح في الوصول إلى الحلّ. ولكن يمكن أن تكون المعلومات التي حصلت عليها في أغلب الأحيان معلومات مهمّة وقيّمة للشخص الذي ستطلب مساعدته، وقد يعينكما ذلك على حلّ المشكلة بسرعة أكبر. ولكن ينبغي عليك طلب المساعدة في بعض اﻷحيان لن تسعفك الدقائق الخمس عشرة في إيجاد الحلّ لمشكلة معينة، وقد لا يسعفك البحث ليوم كامل كذلك. في مثل هذه الحالات، ستجبرك القاعدة على طلب المساعدة. من اليسير جدًّا أن تنغمس إلى أذنيك في حلّ المشكلة، ولكن ينبغي عليك أن تسأل نفسك حينها: “أأنا الشخص المناسب لحلّ هذه المشكلة؟”. يقول المثل: الوقت كالسيف إن لم تقطعه قطعك. قد تشعر بالرضا والفخر بعد أن تبذل مجهودًا كبيرًا في تعلّم أمور جديدة - وستحصل على خبرة كبيرة على المدى الطويل - ولكن لا تتوقع مطلقًا أن تنال الثناء على قضاء ساعات في حلّ مشكلة يمكن لغيرك أن يحلّها في عشر دقائق. كيف يمكن لقاعدة الـ 15 دقيقة أن تساعد الفريق برمّته تقدّم قاعدة الدقائق الخمس عشرة لفرق دعم العملاء أو الفرق الهندسية أو غيرها فوائد جمّة: يتعلّم الموظفون الجدد الاعتماد على أنفسهم. هناك من يستطيع الموظف الجديد الاستعانة به عندما لا يجدّ حلًّا للمشكلة التي يحاول معالجتها. يمكن لهؤلاء الموظفين أن يستشيروا الموظفين القدماء ليتعرفوا على اﻷسلوب الذي يتبعونه في حل المشكلات. يراجع الموظفون القدماء بصورة غير رسمية عمل الفريق ويقدّمون تغذيتهم الراجعة. تزداد خبرة فريق دعم العملاء دون أن يضطر العملاء إلى الانتظار لفترات طويلة للحصول على اﻹجابات. على الرغم من أني لم أعد أعمل مهندسًا (كما أني لست مطّلعًا على جميع التوثيقات)، إلا أنّي ألجأ بصورة منتظمة إلى قاعدة الدقائق الخمس عشرة، فهي تعلّمني احترام وقت زملائي، وتشجّعني في الوقت ذاته على طلب المساعدة عندما أكون محتاجًا إليها. لذا أدعوك لأنّ تجرّب هذه القاعدة وأن تشاركنا النتائج في التعليقات. ترجمة - وبتصرّف - للمقال Know when to ask for help with the 15 Minute Rule لصاحبه Martin Brennan. حقوق الصورة البارزة محفوظة لـ Freepik
  2. إنّ مطالعتك لهذا المقال يعني على الأرجح أنّك قد أتممت الخطوة الأولى في رحلتك كمستقلّ يعمل على شبكة الإنترنت، ألا وهي إيجاد العملاء. وكم نتمنى أن تكون الرحلة قد انتهت عند هذه النقطة، ولكن يؤسفني القول أنّك لا زلت بعيدًا جدًّا عن نقطة النهاية حتى بعد العثور على شخص يرغب في دفع المال لقاء الخدمات التي تقدّمها. بل يمكن القول أنّك الآن تخوض الجزء الأصعب من الرحلة، إذ يجب عليك التعامل مع العملاء مع الحرص على: أن يكون عملاؤك سعداء. أنا لا تصاب بالجنون (وهذه هو الأهمّ، رغم أنّنا ننسى ذلك بعض الأحيان). التعامل مع العملاء أشبه ما يكون بسير البهلوان على الحبل، فمن جانب ترغب في إسعاد عملائك مهما كلّف ذلك، ومن جانب آخر، لا ترغب في أن يتّصل بك العميل عبر Skype في الساعة العاشرة مساءً ليلة السبت، أليس كذلك؟ لا أنكر وجود عملاء جيّدين يحترمون الوقت ويكون التعامل معهم سهلًا ومريحًا، ولكن هناك عملاء متطلّبون للغاية، ويدفعونك إلى اقتلاع شعر رأسك، وأنا متأكد من أنّ لدى كلّ منا العديد من القصص والتجارب التي يمكن أن يرويها في هذا الصدد. في هذا المقال سأتحدّث عن كيفية إدارة علاقاتك مع عملائك بصورة أفضل ومن جانبين مختلفين. وستكون قادرًا بعد الانتهاء من مطالعة المقال على إسعاد عملائك دون التفريط بسعادتك الشخصية. كيف تحافظ على سعادة عملائك إن عدم وجود عملاء سعداء يعني أنّك لن تحصل على لقمة العيش، وهذا ليس أمرًا جيّدًا بحدّ ذاته. لذا إليك بعض النصائح - غير تلك النصائح الواضحة مثل إنجاز الأعمال بجودة عالية واحترافية كبيرة - التي تساعدك في المحافظة على سعادة عملائك: أرسل مستجدّات المشروع بصورة استباقية لقد وجدت خلال مسيرتي المهنيّة كمستقلّ أنّ إرسال رسائل إلكترونية استباقية إلى العميل بخصوص مستجدّات المشروع أمرٌ مفيدٌ للغاية. وأعني بالرسائل الاستباقية، رسائل سريعة مثل: “أود إعلامك بأني قد أكملت الخطوة س من المشروع وسأرسلها إليك في الموعد ص كما هو مقرّر”. إن كنت قد وضعت موعدًا لإنجاز العمل، فإن العميل سيتوقع منك إتمام العمل في ذلك الموعد، ولكن عندما لا يكون العمل وجهًا لوجه، سيشعر العميل بعدم الراحة والخوف من عدم الالتزام بموعد اﻹنجاز المحدّد. تساعد هذه الرسائل القصيرة على معالجة المشكلة قبل أن تكبر وتتفاقم، وﻻ يتطلّب كتابة مثل هذه الرسائل أكثر من دقيقة واحدة، لذا لا تسمح للشكّ بأن يتسلّل إلى عميلك، بل أطلعه على المستجدّات أوّلًا بأوّل، وأعدك بأنّه سيقدّر ذلك كثيرًا. اخفض سقف الوعود وارفع سقف الإنجازات إن كنت تعتقد بأنّك ستنجز العمل خلال 7 أيام فلا تخبر العميل بذلك على الإطلاق، بل أخبره بأنّك ستنجز العمل خلال 10 أيام. بل الأفضل أن تكون المدّة 14 يومًا. وستنجز بذلك أمرين اثنين: إن كان اعتقادك صحيحًا، فستبدو في نظر عميلك بطلًا خارقًا استطاع إنجاز المشروع في وقت مبكّر. إن كنت مخطئًا في اعتقاد، فسيكون لديك الوقت الكافي لإنجاز المشروع وأنت مرتاح البال. أنجز أكثر مما هو مطلوب منك لا أقصد هنا العمل الإضافي دون مقابل، بل ما أعنيه أنّك على دراية كبيرة بالإنترنت، وهذا يعني أنّك على الأرجح تمتلك معلومات أكثر من عميلك، وبإمكانك الاستفادة من هذه المعلومات في إسعاد العميل. لتقريب المعنى إليك المثال التالي: حتى لو لم تكن خبيرًا في SEO فإنّ معلوماتك حول هذا الموضوع ستفوق على الأرجح معلومات عميلك؛ لذا إن لاحظت وجود مشكلة كبيرة في SEO أثناء القيام بعملك الاعتيادي، لا تتردّد في إطلاع عميلك على المشكلة. لن يكلّفك الأمر سوى رسالة إلكترونية واحدة، أو يمكنك الحديث عن الموضوع خلال مكالمة هاتفية مع العميل، ولكن بالنسبة إلى العميل فسيبدو الأمر وكأنّك تحرص على أن يحقق العميل النجاح مهما كلّفك ذلك. عادة ما يُسعد العملاء بمثل هذه الملاحظات، وستغدو بالنسبة إلى عميلك مستشارًا أكثر من كونك مجرّد منفّذ للعمل. أنا أقدّم لعملائي النصائح والآراء وبصورة دائمة في أمور لا ترتبط بالكتابة، وحسب تجربتي الشخصية، فإن الغالبية العظمى من العملاء يقدّرون هذه المساعدة. احرص فقط على أن تكون منطقيًّا هنا، فإن لم يتجاوب العميل مع اقتراحات ونصائحك منذ البداية فعليك صرف النظر عن الموضوع تمامًا. كيف تحافظ على سلامتك إن كانت طريقتك الوحيدة في إسعاد عملائك تتمثّل في التواصل معهم طوال اليوم وعلى مدار الأسبوع عبر جميع وسائل التواصل المتاحة فستصاب باﻹعياء. قد لا يحدث هذا بادئ الأمر، ولكنّي أعدك أنّه سيحدث حتمًا. تجنّب إذًا الوقوع في هذا الفخّ باتباع النصائح التالية، وإن وضّحت الأمور لعملائك منذ البداية فلن تتأثر علاقتك بهم على الإطلاق وستشعر براحة وسعادة كبيرتين. اجعل عدد قنوات التواصل محدودًا أذكر أن صفحة “تواصل معي” في أيامي الأولى كمستقل كانت تتضمّن استمارة التواصل، وعنوان بريدي الإلكتروني، وحساباتي في Skype وTwitter وإحداثيات GPS الخاصة بي ليتواصل العملاء معي بواسطة الحمام الزاجل… هل فهمت ما أقصد؟ كنت أفكّر أنّني سأكون متوفّرًا للعملاء في أي وقت، وهكذا سيحبّونني بالتأكيد، ولم أشكّ للحظة في مدى صحّة هذه الفكرة، إلى أن وصلتني أول رسالة على Skype في ليلة السبت، أعقبتها رسالة أخرى بعد عشر دقائق لأنني لم أردّ على الرسالة اﻷولى. أما اليوم فصفحة “تواصل معي” تتضمن استمارة وحيدة فريدة: ضع الصورة نعم، هذه هي وسيلة التواصل الوحيدة. أسعى من خلال هذه الاستمارة إلى أن أقلّص قدر الإمكان قنوات التواصل إلى نطاق البريد الإلكتروني أو Trello. لا مشكلة على الإطلاق في أن تستخدم طرق تواصل مختلفة، ولكن وبحسب خبرتي في هذا المجال يفضّل أن تتبع هاتين القاعدتين: اختر وسيلة التواصل المفضّلة لديك والتزم بها، فإن كنت تحبّ Skype وجّه كل عمليات التواصل باتجاهه والتزم بذلك. افصل وسيلة التواصل هذه عن حساباتك الشخصية. فعلى سبيل المثال، لو كنت تملك حسابًا على Skype تتواصل من خلاله معه الأهل والأصدقاء، أنشئ حسابًا آخر مخصّصًا للعمل. بهذه الطريقة، لن تقع تحت وابل من رسائل العمل لمجرد أنّك ترغب في التواصل مع عائلتك في عطلة نهاية الأسبوع (هذا ما حدث معي في بداياتي كمستقلّ). الفائدة الثالثة هي أنّ الالتزام بوسيلة تواصل محدّدة يسهّل عليك الرجوع إلى المحادثات السابقة في حال نسيت بعض التفاصيل المتعلّقة بالمشروع. حدّد المدّة المتوقّعة للاستجابة بعد أن أزلتُ جميع وسائل التواصل الزائدة من صفحة “تواصل معي”، أضفت رسالة تظهر مباشرة بعد أن يقوم العميل بتعبئة الاستمارة وإرسالها. مضمون الرسالة هو: “شكرًا على تواصلك معي. سأجيبك خلال الـ 24 ساعة القادمة باستثناء عطلة نهاية الأسبوع”. ضع الصورة ستتعب كثيرًا إن حاولت البقاء على تواصل مع عملائك طوال اليوم، وهذا أمر لا شكّ فيه، ولكن سيتوقع الكثير من العملاء أنّك ستجيب على رسائلهم في أوقات غريبة من اليوم، إلا إذا بيّنت لهم ومنذ البداية الوقت المتوقّع للتواصل. لذا، حدّد الفترة التي تناسبك للتواصل مع العملاء ثم بيّن ذلك لهم منذ البداية. لا أذكر أنّ أحد من العملاء قد رفض الانتظار لمدة 24 ساعة ليحصل على الإجابة ما دمت قد بيّنت له ذلك منذ البداية. مع ذلك يجب عليك الاستجابة لرسائل العميل بالسرعة الممكنة، إذ سيشعر العميل بقليل من الاستياء إن أصبحت مدة الاستجابة لديك 24 ساعة بعد أن كانت ساعتين فقط. كن منظّمًا الطريقة الأخيرة للمحافظة على سلامتك هي اعتماد طريقة عمل منظّمة وتطبيقها مع جميع العملاء. وأفضل طريقة للاطلاع الكامل على ما يدور بينك وبين عميلك هو الاحتفاظ بجميع مواعيد الإنجاز واللقاءات في مكان واحد. لا أحبّذ هنا تفضيل طريقة على أخرى، إذ يبدو أنّ لكل شخص ما طريقته المفضّلة. أنا أستخدم [Todoist](Keep Yourself Sane)، ولكني أعرف الكثير من الأشخاص الذي يفضّلون استخدام أدوات ذات مزايا أكبر لإدارة المشاريع مثل: Asana Basecamp Trello ليس المهمّ الاعتماد على منصّة معينة، بل المهمّ أنّك تستخدم إحدى الأدوات لإدارة مشاريعك وسير عملك، فسرعان ما تقع تحت ضغط عمل شديد ما إن تتذكّر موعد إنجاز مشروع كنت قد نسيته تمامًا. الخلاصة إضافة إلى كل ما ورد أعلاه، أرجو منك أن تتّبع أساسيات النجاح في إدارة علاقاتك مع العملاء: اطلب عقدًا موقّعًا في حال عملك على مشاريع كبيرة. ناقش بوضوح مدى قدرتك على إنجاز العمل. احرص على تقديم عمل جيد. سيساعدك اتباع هذه الأساسيات في إسعاد عملائك دون التفريط بسعادتك وصحّتك وحياتك الشخصية. إن كنت تملك نصائح أخرى لإدارة العلاقة مع العملاء فلا تبخل بها علينا؛ فإضافة إلى تقديم المساعدة لكلّ من يقرأ هذا المقال، أحبّ دومًا أن أنظّم أساليبي الخاصة في العمل. ترجمة - وبتصرّف - للمقال Managing Clients: How to Keep Them Happy Without Pulling Your Hair Out لصاحبه Colin Newcomer. حقوق الصورة البارزة محفوظة لـ Freepik
  3. هذا هو الجزء الأخير من سلسلة “مدخل إلى إطار العمل Ruby on Rails” وفي هذا الجزء سنعيد هيكلة الشيفرة التي كتبناها في الأجزاء السابقة من السلسلة، وسنتعرّف إلى نظام الاستيثاق البسيط الذي يقدّمه إطار العمل Rails. إعادة هيكلة الشيفرة بعد أن أصبحت المقالات والتعليقات تعمل بصورة جيدة، لنلقِ نظرة على القالب app/views/articles/show.html.erb . يبدو الملف طويلًا جدًّا، لذا سنستخدم الملفات الجزئية لتنظيف وترتيب الشيفرة البرمجية. تصيير مجموعة الملفات الجزئية في البداية سننشئ ملفًّا جزئيًا خاصًّا بالتعليقات وظيفته عرض جميع التعليقات الخاصّة بالمقالة. أنشئ الملف app/views/comments/_comment.html.erb وأضف إليه الشيفرة التالية: <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> والآن يمكنك تعديل الملف `app/views/articles/show.html.erb` كما يلي: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <%= render @article.comments %> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> بهذه الطريقة سيتم تصيير الملف الجزئي في app/views/comments/_comment.html.erb لكلّ تعليق موجود في مجموعة @article.comments، وعندما يتنقّل التابع render بين عناصر مجموعة التعليقات فإنه يُسند كل تعليق إلى متغيّر محلي local variable يحمل اسم الملف الجزئي ذاته، وفي حالتنا هذه comment والذي يكون متوفّرًا في الملف الجزئي. تصيير الملف الجزئي الخاصّ بالاستمارة لنقم بإزالة قسم التعليقات الجديد إلى ملف جزئي خاصّ به، ومرة أخرى أنشئ ملفًّا باسم _form.html.erb في المجلد app/views/comments/ وأضف إليه ما يلي: <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> ثم عدّل الملف app/views/articles/show.html.erb ليصبح بالصورة التالية: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <%= render @article.comments %> <h2>Add a comment:</h2> <%= render 'comments/form' %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> يعرّف تابع render الثاني القالب الجزئي الذي نرغب في تصييره وهو comments/form، ونظرًا لوجود المحرّف / ضمن هذه السلسلة النصّية سيعرف Rails بأنّك ترغب في تصيير الملف _form.html.erb الموجود في المجلد app/views/comments. أما الكائن @article فسيكون متوفّرًا لأيّ ملفّ جزئي يتم تصييره في العرض لأنّنا عرّفناه كمتغيّر من نوع instance. حذف التعليقات إن القدرة على حذف التعليقات المزعجة هي من الميزات المطلوب توفرها في المدوّنة، ولتنفيذ ذلك سنحتاج إلى إضافة رابط لحذف التعليقات ضمن العرض وإلى حدث destroy في المتحكّم CommentsController. لذا سنضيف أوّلًا رابط الحذف ضمن الملفّ الجزئي app/views/comments/_comment.html.erb وكما يلي: <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> <p> <%= link_to 'Destroy Comment', [comment.article, comment], method: :delete, data: { confirm: 'Are you sure?' } %> </p> سيؤدّي النقر على هذا الرابط إلى إرسال الفعل DELETE متمثّلًا بالرابط /articles/:article_id/comments/:id إلى المتحكّم CommentsController، والذي سيبحث بدوره - مستعينًا بهذا الرابط - عن التعليق المراد حذفه من قاعدة البيانات. لنضِف حدث destroy إلى المتحكّم في الملف app/controllers/comments_controller.rb: class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) redirect_to article_path(@article) end def destroy @article = Article.find(params[:article_id]) @comment = @article.comments.find(params[:id]) @comment.destroy redirect_to article_path(@article) end private def comment_params params.require(:comment).permit(:commenter, :body) end end سيبحث الحدث destroy عن التعليق المراد حذفه، ثم يعيّن موقعه في مجموعة @article.comments ثم يحذفه من قاعدة البيانات ويعيد توجيهنا إلى حدث show الخاصّ بالمقالة. حذف الكائنات المترابطة من البديهي أنّه عند حذف مقالة معيّنة فإن من الواجب أن يتم حذف التعليقات المرتبطة بها، وإلا فستشغل هذه التعليقات مساحة ضمن قاعدة البيانات دون أيّ فائدة. يتيح لنا Rails استخدام الخيار dependent لتحقيق ذلك. توجّه إلى نموذج Article (app/models/article.rb) وعدّله بالصورة التالية: class Article < ApplicationRecord has_many :comments, dependent: :destroy validates :title, presence: true, length: { minimum: 5 } end الاستيثاق Authentication في Rails إن كنت ترغب في نشر المدوّنة على الإنترنت، سيكون بإمكان أي شخص إضافة وتعديل وحذف المقالات والتعليقات فيها. يقدّم Rails نظام استيثاق HTTP بسيط يمكن استخدامه في التطبيقات البسيطة كتطبيقنا هذا. سنحتاج في المتحكّم ArticlesController إلى وسيلة لمنع وصول الشخص غير المستوثق منه إلى الأحداث التي يتضمّنها هذا المتحكّم، ويمكن استخدام تابع http_basic_authenticate_with لتحقيق ذلك. ولاستخدام نظام الاستثياق سنقوم بالإفصاح عنه في بداية ملف المتحكّم ArticlesController in app/controllers/articles_controller.rb وسنستوثق من جميع الأحداث المتوفّرة في هذا المتحكّم عدا حدثي index وshow: class ArticlesController < ApplicationController http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show] def index @articles = Article.all end # بقيّة الشيفرة ... كذلك سنسمح للمستخدمين المستوثق منهم فقط بحذف التعليقات، لذا أضف الشيفرة التالية إلى المتحكّم CommentsController في الملف app/controllers/comments_controller.rb: class CommentsController < ApplicationController http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy def create @article = Article.find(params[:article_id]) # ... end # بقيّة الشيفرة ... والآن إن حاولت إنشاء مقالة جديدة، ستتلقّى طلب استيثاق كهذا: جدير بالذكر أنّ هناك العديد من وسائل الاستيثاق في تطبيقات Rails، أشهرها Devise rails engine و Authlogic. إطار العمل Rails ونظام الترميز UTF-8 إن أسهل طريقة للعمل مع Rails هي تخزين جميع البيانات الخارجية بنظام الترميز UTF-8، وإن لم تفعل ذلك فغالبًا ما تقوم مكتبات Ruby وإطار العمل Rails بتحويل البيانات الأصلية إلى هذا الترميز، ولكن لا يمكن الاعتماد على هذه المكتبات بصورة تامّة، ويفضّل التأكد من أنّ جميع البيانات الخارجية مرمّزة بهذا النظام. وفي حال حدوث أي خطأ في نظام الترميز فإن الحروف ستظهر في المتصفّح غالبًا على هيئة أشكال معينية سوداء بداخلها علامة استفهام، أو قد تظهر الحروف على هيئة رموز غريبة كأن يظهر الرمز “ü” بدلاً من الحرف “ü”. يتّخذ Rails بعض الإجراءات في نظامه الداخلي للتقليل من المسبّبات الشائعة لهذه المشاكل والتي يمكن الكشف عنها وتصحيحها بصورة تلقائية. ولكن، إن كنت تتعامل مع بيانات من مصادر خارجية غير مخزّنة بترميز UTF-8، لن يكون Rails قادرًا على الكشف بصورة تلقائية عن أسباب المشكلة أو تقديم حلّ لها. وهناك مصدران شائعان للبيانات غير المخزّنة بترميز UTF-8: محرر النصوص: تحفظ معظم محرّرات النصوص الملفات البرمجية بصيغة UTF-8، وإن لم يقم محرّر النصوص الذي تستخدمه في كتابة الشيفرات البرمجية بذلك، فقد ينتج عن ذلك تحوّل الحروف الخاصّة أو حروف اللغات الأخرى غير الإنكليزية إلى التحول في المتصفّح إلى أشكال معينية بداخلها علامة استفهام. ينطبق هذا الأمر كذلك على ملفات الترجمة i18n. تجدر الإشارة إلى أنّه تتيح معظم محررات النصوص التي لا تحفظ الملفات البرمجية بهذا الترميز افتراضيًّا (مثل Dreamweaver) إمكانية تغيير الترميز الافتراضي للملفات المحفوظة إلى نظام UTF-8، وننصح بالقيام بذلك. قاعدة البيانات: يحوّل Rails البيانات القادمة من قاعدة البيانات إلى ترميز UTF-8، ولكن إن لم يكن هذا نظام الترميز هذا مستخدمًا من طرف قاعدة البيانات فلن يكون بالإمكان تخزين جميع المحارف المدخلة من قبل المستخدم. فعلى سبيل المثال، إن كان نظام الترميز الداخلي لقاعدة البيانات هو Latin-1 وأدخل المستخدم كلمات باللغة الروسية أو العربية أو اليابانية، فستخسر البيانات إلى الأبد بمجرد دخولها إلى قاعدة البيانات. لذا ينصح دائمًا بتحويل نظام الترميز الداخلي في قاعدة البيانات إلى نظام UTF-8. المصدر: توثيقات Ruby on Rails.
  4. تحدّثنا في الجزء السابق من هذه السلسلة عن النماذج في إطار العمل Ruby on Rails وتعرّفنا على طريقة إنشائها والتعامل معها من خلال كتابة الشيفرة المسؤولة عن حفظ المقالة الجديدة في قاعدة البيانات. في الجزء الثاني من هذا الموضوع سنتعلّم كيفية ربط نموذجين مع بعضهما البعض من خلال إنشاء نموذج جديد خاصّ بالتعليقات. ولكن قبل ذلك سنكمل ما بدأناه في الدروس السابقة من السلسلة في بناء عمليات “CRUD” حيث غطّينا سابقًا عمليتي الإنشاء Create و القراءة Read، وسنغطي اليوم العمليتين المتبقّيتين وهما التحديث Update والحذف Destroy. تحديث المقالات الخطوة الأولى في عملية تحديث المقالات هي إضافة حدث edit إلى المتحكم ArticlesController بين حدثي new و create وكما يلي: def new @article = Article.new end def edit @article = Article.find(params[:id]) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end سيتضمن العرض استمارة مشابهة لتلك التي استخدمناها في إنشاء المقالات الجديدة. أنشئ ملفًّا باسم app/views/articles/edit.html.erb وأضف إليه الشيفرة التالية: <h1>Edit article</h1> <%= form_for(@article) do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', articles_path %> سنوجّه الاستمارة هذه المرة إلى حدث update والذي لم نقم بتعريفه حتى الآن. يؤدي تمرير كائن المقالة للتابع إلى إنشاء عنوان url لإرسال استمارة المقالة التي تم تعديلها، ومن خلال هذا الخيار نخبر Rails بأنّنا نرغب في أن يتم إرسال هذا النموذج من خلال فعل HTTP PATCH وهو أحد أفعال HTTP التي تستخدم في تحديث الموارد حسب بروتوكول REST. يمكن أن يكون المعامل الأول لـ form_for كائنًا، مثلًا @articl، والذي سيؤدي بالدالة المساعدة إلى ملء الاستمارة بالحقول التابعة للكائن، ويؤدي تمرير الرمز (:article) بنفس اسم المتغيّر من نوع instance (@article) إلى نفس النتيجة تلقائيًا. والآن سنقوم بإنشاء الحدث update في المتحكّم app/controllers/articles_controller.rb وسنضيفه بين حدث create والتابع ذي المحدّد الخاصّ private: def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end def update @article = Article.find(params[:id]) if @article.update(article_params) redirect_to @article else render 'edit' end end private def article_params params.require(:article).permit(:title, :text) end يستخدم الحدث update عندما ترغب في تحديث سجل موجود في قاعدة البيانات، ويستقبل هذا الحدث جدول تقطيع hash يحتوي الخصائص التي ترغب في تحديثها. وكما سبق، في حال وجود خطأ في عملية التحديث سنعرض الاستمارة على المستخدم من جديد. سنستخدم التابع article_params الذي عرّفناه في وقت سابق للحدث create. لا حاجة لتمرير جميع الخصائص لغرض تحديثها، فعلى سبيل المثال، إن تم استدعاء @article.update(title: 'A new title') فسيقوم Rails بتحديث خاصية العنوان فقط، ويترك باقي الخصائص دون تعديل. أخيرًا، نرغب في عرض رابط إلى الحدث edit في الصفحة التي نعرض فيها قائمة المقالات، لذا توجّه إلى الملف app/views/articles/index.html.erb وأضف الرابط ليظهر إلى جانب رابط “Show”: <table> <tr> <th>Title</th> <th>Text</th> <th colspan="2"></th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> <td><%= link_to 'Edit', edit_article_path(article) %></td> </tr> <% end %> </table> سنضيف كذلك رابطًا إلى قالب app/views/articles/show.html.erb ليظهر رابط “Edit” في صفحة المقالة أيضًا: ... <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> هذا هو شكل تطبيقنا حتى هذه اللحظة: استخدام الملفات الجزئية partials لإزالة التكرار من العروض تبدو صفحة تحرير المقالة مشابهة تمامًا لصفحة إنشاء مقالة جديدة، وفي الواقع تستخدم الصفحتان الشيفرة ذاتها لعرض الاستمارة. سنقوم الآن بالتخلص من هذا التكرار باستخدام ملفات العرض الجزئية. تحمل هذه الملفات أسماء تبدأ بالمحرف (_). أنشئ ملفًّا جديدًا باسم _form.html.erb ضمن المسار app/views/articles/ وأضف إليه الشيفرة التالية: <%= form_for @article do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> لاحظ أننا لم نغيّر شيئًا باستثناء الإعلان عن التابع form_for وسبب استخدام هذه الأسلوب المختصر والبسيط في الإعلان عن التابع form_for للتعبير عن الاستمارتين هو أن @article عبارة عن مورد يرتبط بمجموعةٍ من مسارات RESTful، وبإمكان Rails أن يخمّن عنوان URI والتابع الذي يجب استخدامه. والآن لنقم بتحديث العرض app/views/articles/new.html.erb لاستخدام الملف الجزئي الذي أنشأناه وسنقوم بإعادة كتابة العرض من جديد وكما يلي: <h1>New article</h1> <%= render 'form' %> <%= link_to 'Back', articles_path %> ثم قم بالأمر عينه في ملف العرض app/views/articles/edit.html.erb: <h1>Edit article</h1> <%= render 'form' %> <%= link_to 'Back', articles_path %> حذف المقالات هذه هي العملية الأخيرة ضمن عمليات CRUD، وبحسب معايير REST فإن المسار الذي يؤدي إلى حذف المقالات وكما يظهر في مخرجات الأمر bin/rails routes هو: DELETE /articles/:id(.:format) articles#destroy يجب استخدام الفعل DELETE في المسار المسؤول عن حذف الموارد، أما في حال استخدام الفعل GET فسيكون بالإمكان إنشاء رابط خبيث كهذا الرابط مثلًا: <a href='http://example.com/articles/1/destroy'>look at this cat!</a> سنستخدم التابع delete لحذف المصادر، وهذا المسار مرتبط بالحدث destroy ضمن المتحكّم app/controllers/articles_controller.rb والذي لم نقم بإنشائه بعد. عادة ما يكون التابع destroy التابع الأخير ضمن المتحكّم، وكما هو الحال مع بقية التوابع العامّة public يجب الإعلان عنه قبل أي توابع خاصّة أو محميّة protected. def destroy @article = Article.find(params[:id]) @article.destroy redirect_to articles_path end الصورة النهائية للمتحكّم ArticleController في الملف app/controllers/articles_controller.rb هي: class ArticlesController < ApplicationController def index @articles = Article.all end def show @article = Article.find(params[:id]) end def new @article = Article.new end def edit @article = Article.find(params[:id]) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end def update @article = Article.find(params[:id]) if @article.update(article_params) redirect_to @article else render 'edit' end end def destroy @article = Article.find(params[:id]) @article.destroy redirect_to articles_path end private def article_params params.require(:article).permit(:title, :text) end end يمكن استدعاء التابع destroy في كائنات التسجيلة النشطة Active Record عندما ترغب في حذفها من قاعدة البيانات. لاحظ أنّنا لسنا بحاجة لإضافة عرض خاص بهذا الحدث لأنّنا نعيد توجيه المستخدم إلى الحدث index. أخيرًا أضف رابط ‘Destroy’ إلى القالب app/views/articles/index.html.erb لنربط كل الصفحات مع بعضها البعض. <h1>Listing Articles</h1> <%= link_to 'New article', new_article_path %> <table> <tr> <th>Title</th> <th>Text</th> <th colspan="3"></th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> <td><%= link_to 'Edit', edit_article_path(article) %></td> <td><%= link_to 'Destroy', article_path(article), method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> استخدمنا هنا التابع link_to بطريقة مختلفة، حيث مررّنا اسم المسار كمعامل ثانٍ، ثمّ مرّرنا الخيارات الأخرى بعد ذلك. تستخدم الخيارات method: :delete وdata: { confirm: 'Are you sure?' } كخصائص HTML5 بحيث يؤدي الضغط على الرابط إلى عرض مربع حوار للتأكد من رغبة المستخدم في حذف المقالة، ثم إرسال الرابط باستخدام التابع delete. تتمّ تعملية التحقّق هذه بواسطة ملف JavaScript الذي يحمل الاسم rails-ujs والموجود بصورة افتراضية في مخطط التطبيق (app/views/layouts/application.html.erb)، وفي حال عدم وجود هذا الملف لن يظهر مربع الحوار التأكيدي للمستخدم. تهانينا أصبح بإمكانك الآن إنشاء وعرض وسرد وتحديث وحذف المقالات في مدوّنتك. إضافة النموذج الخاصّ بالتعليقات سنقوم الآن بإنشاء نموذج جديد في تطبيقنا هذا ستكون وظيفته التعامل مع التعليقات. إنشاء النموذج لإنشاء النموذج الخاص بالتعليقات سنتبع الأسلوب السابق نفسه وذلك باستخدام أداة المولّد لإنشاء نموذج يحمل الاسم Comment ويمثّل مرجعًا إلى المقالة. اكتب الأمر التالي في سطر الأوامر: $ bin/rails generate model Comment commenter:string body:text article:references سينشئ هذا الأمر أربعة ملفات: الملف Purpose الملف/المجلد الوظيفة db/migrate/20140120201010_create_comments.rb ملف التهجير المسؤول عن إنشاء جدول التعليقات في قاعدة البيانات (سيحمل اسم الملف لديك ختمًا زمنيًا مختلفًا) app/models/comment.rb النموذج الخاص بالتعليقات test/models/comment_test.rb ملف الاختبارات الخاص بنموذج التعليقات test/fixtures/comments.yml نماذج تعليقات تستخدم في إجراء الاختبارات لنلق نظرة في البداية على ملف app/models/comment.rb: class Comment < ApplicationRecord belongs_to :article end كما تلاحظ فمحتوى هذا الملف مشابه لنموذج Article الذي أنشأناه سابقًا، والفارق الوحيد هو السطر belongs_to :article والذي ينشئ رابطًا بين النموذجين، وسنتحدّث عن الروابط بعد قليل. أما الكلمة المفتاحية (:references) ضمن الأمر الذي قمنا بتنفيذه في سطر الأوامر، فهي نوع خاص من البيانات بالنسبة للنماذج. تنشئ هذه الكلمة المفتاحية عمودًا في الجدول الموجود في قاعدة البيانات يحمل اسم النموذج الذي تمّ تمريره إلى هذه الكلمة مع إضافة _id والذي يمثّل عددًا صحيحًا. ستتضح الأمور أكثر بالنسبة إليك إن تفحّصت ملف db/schema.rb أدناه. قام Rails - بالإضافة إلى إنشاء النموذج - بإنشاء تهجير وظيفته إنشاء الجدول المقابل للنموذج في قاعدة البيانات: class CreateComments < ActiveRecord::Migration[5.0] def change create_table :comments do |t| t.string :commenter t.text :body t.references :article, foreign_key: true t.timestamps end end end يُنشئ السطر t.references عمودًا من نوع integer باسم article_id إضافة إلى فهرس index خاص بهذا العمود وقيد مفتاح خارجي Foreign Key Constraint والذي يشير إلى عمود id في جدول المقالات. والآن نفذ التهجير باستخدام الأمر التالي: $ bin/rails db:migrate ينفّذ Rails التهجيرات غير المنفّذة فقط؛ لذا ستكون نتيجة الأمر التالي كما يلي: == CreateComments: migrating ================================================= -- create_table(:comments) -> 0.0115s == CreateComments: migrated (0.0119s) ======================================== ربط النماذج مع بعضها البعض تسهّل روابط التسجيلة النشطة تكوين العلاقات بين النماذج، وفي حالتنا هذه سنُنشئ علاقة بين جدولي التعليقات والمقالات، ولو فكّرنا في طبيعة العلاقة التي تربط بينهما فسنجد أنه: ينتمي كل تعليق إلى مقالة واحدة. تمتلك المقالة الواحدة العديد من التعليقات. يستخدم Rails صياغة مشابهة للربط بين النماذج، وقد شاهدنا في نموذج Comment في الملف app/models/comment.rb الشيفرة المسؤولة عن ربط كل تعليق بمقالة واحدة: class Comment < ApplicationRecord belongs_to :article end سنحتاج الآن إلى تكوين الجانب الثاني من الرابطة، أي ربط المقالات بالتعليقات، لذا توجّه إلى الملف app/models/article.rb وعدّله بالصورة التالية: class Article < ApplicationRecord has_many :comments validates :title, presence: true, length: { minimum: 5 } end والآن أصبح النموذجان مرتبطين مع بعضهما البعض تلقائيًا، فعلى سبيل المثال، في حال كان لدينا متغيّر @article والذي يمثّل مقالة معيّنة، يمكن استدعاء جميع التعليقات المرتبطة بتلك المقالة على هيئة مصفوفة وذلك من خلال @article.comments. إضافة مسار خاص بالتعليقات كما هو الحال مع متحكم welcome سنحتاج إلى إضافة مسار نحدّد من خلاله العنوان الذي نرغب في استخدامه لمشاهدة التعليقات؛ لذا افتح ملف config/routes.rb مرة أخرى، وعدّله كما يلي: resources :articles do resources :comments end بهذه الطريقة تصبح التعليقات بمثابة موارد مضمّنة في المقالات، وهذه الطريقة هي جزء من العلاقة الهرمية التي تنشأ بين المقالات والتعليقات. إنشاء المتحكّم الخاصّ بالتعليقات بعد أن انتهينا من إعداد النموذج، أصبح بإمكاننا الآن إنشاء المتحكّم الخاص بالتعليقات، وسنستخدم أداة المولّد كما فعلنا سابقًا: $ bin/rails generate controller Comments سينشئ هذا الأمر خمسة ملفات إضافة إلى مجلّد فارغ: الملف/المجلد الوظيفة app/controllers/comments_controller.rb المتحكّم الخاص بالتعليقات /app/views/comments يتم تخزين العروض الخاصّة بالتعليقات في هذا المجلد test/controllers/comments_controller_test.rb ملف الاختبار الخاصّ بالمتحكّم app/helpers/comments الملف الخاصّ بمساعد العرض app/assets/javascripts/comments.coffee ملف CoffeScript الخاصّ بالمتحكّم app/assets/stylesheets/comments.scss أوراق الأنماط المتتالية CSS الخاصّة بالمتحكّم كما هو الحال مع أي مدوّنة، فإن القرّاء سيكتبون تعليقاتهم بعد قراءة المقالة مباشرة، وبعد أن يرسلوا تعليقاتهم يتم توجيههم إلى صفحة عرض المقالة ليتمكّنوا من مشاهدة التعليقات. وستكون وظيفة المتحكّم CommentsController هي توفير التوابع اللازمة لإنشاء التعليقات وحذف التعليقات المزعجة حال وصولها. سنقوم أولًا بتعديل قالب عرض المقالات app/views/articles/show.html.erb لنتمكن من إضافة تعليق جديد: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> ستضيف الشيفرة السابقة استمارة إلى صفحة عرض المقالات يمكن من خلالها إضافة تعليق جديد من خلال استدعاء الحدث create ضمن المتحكّم CommentsController. ويستخدم الاستدعاء form_for مصفوفة ستعمل على إنشاء مسار متداخل nested route مثل: /articles/1/comments. لنجرِ الآن التعديلات اللازمة على الحدث create في الملفّ app/controllers/comments_controller.rb: class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) redirect_to article_path(@article) end private def comment_params params.require(:comment).permit(:commenter, :body) end end ستتعقّد الأمور هنا قليلًا وذلك بسبب التداخل nesting الحاصل بين المسارات، إذ في كل مرة يتمّ فيها طلب تعليق معيّن يجب أن يتابع ذلك الطلب المقالة التي يرتبط بها هذا التعليق، وبالتالي استدعاء التابع find في نموذج Article والمسؤول عن اختيار المقالة المطلوبة حسب المعرّف المحدّد في المسار. بالإضافة إلى ذلك، استفدنا من بعض التوابع التي تقدّمها عملية الربط بين النموذجين، فقد استخدمنا التابع create على @article.comments لإنشاء التعليق وحفظه، وسيؤدي هذا إلى ربط التعليق الجديد بالمقالة المحدّدة. وبعد إنشاء التعليق الجديد نعيد توجيه المستخدم إلى المقالة الأصلية باستخدام الدالة المساعد article_path(@article). وكما شاهدنا تستدعي هذه الدالة الحدث show ضمن المتحكّم ArticlesController والذي يعمل بدوره على تصيير القالب show.html.erb، وهو المكان الذي نرغب أن تظهر التعليقات فيه؛ لذا سنقوم بإجراء التعديلات اللازمة على الملف app/views/articles/show.html.erb. <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <% @article.comments.each do |comment| %> <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> <% end %> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> أصبح بإمكانك الآن إضافة المقالات والتعليقات إلى مدوّنتك وعرضها في الأماكن الصحيحة. في الدرس القادم سنكمل العمل على التعليقات، حيث سنستخدم الملفات الجزئية لترتيب القوالب أوّلًا، ثم نضيف إمكانية حذف التعليقات من قاعدة البيانات، وفي الختام سنتطرّق إلى عملية الاستيثاق Authentication بصورة سريعة ومبسّطة. المصدر: توثيقات Ruby on Rails.
  5. تعرّفنا في الدرس السابق بإيجاز على طريقة عمل إطار العمل Ruby on Rails حيث تعرّفنا على المتحكّمات والعروض وبدأنا بإنشاء تطبيق المدوّنة البسيطة وأنشأنا في الدرس السابق الاستمارة الخاصة بإضافة مقالة جديدة، ولكن وصلنا إلى النقطة التي نحتاج فيها إلى تخزين المقالة في قاعدة البيانات، وهنا يأتي دور النماذج Models. تحمل النماذج في Rails اسمًا بصيغة المفرد في حين يحمل الجدول المرتبط بها في قاعدة البيانات اسمًا بصيغة الجمع. تتيح أداة المولّد generator في Rails إنشاء النماذج ويلجأ أغلب المطوّرين إلى هذه الأداة لإنشاء النماذج. لإنشاء نموذج جديد استخدم الأمر التالي في سطر الأوامر: $ bin/rails generate model Article title:string text:text من خلال هذه الأمر نخبر Rails بأننا نرغب في إنشاء نموذج باسم Article إلى جانب خاصّية title من نوع string، وخاصّية text من نوع text. تضاف هذه الخواص بصورة تلقائية إلى جدول المقالات في قاعدة البيانات ويتم ربطها بنموذج Article. ويستجيب Rails لهذا الأمر بإنشاء عدد من الملفات، وما يهمّنا منها الآن هما app/models/article.rb و db/migrate/20140120191729_create_articles.rb (لاحظ أن اسم الملف الثاني يختلف قليلًا عن هذا الاسم). الملف الثاني مسؤول عن إنشاء بنية قاعدة البيانات، وهو ما سنتحدث عنه بعد قليل. إجراء عملية التهجير Migration كما لاحظنا فإن الأمر bin/rails generate model قد أنشأ ملف تهجير لقاعدة البيانات داخل المجلد db/migrate. والتهجيرات هي عبارة عن أصناف مصمّمة لتسهيل عملية إنشاء الجداول في قواعد البيانات والتعديل عليها. يستخدم Rails أوامر rake لإجراء التهجيرات، ويمكن التراجع عن عملية التجهير بعد إجرائها على قاعدة البيانات. تتضمّن أسماء ملفات التهجير ختمًا زمنيًا لضمان معالجة هذه الملفات حسب التسلسل الزمني لإنشائها. لو ألقينا نظرة في ملف db/migrate/YYYYMMDDHHMMSS_create_articles.rb (تذكّر أن الملف عندك يحمل ختمًا زمنيًّا مختلفًا) فسنجد التالي: class CreateArticles < ActiveRecord::Migration[5.0] def change create_table :articles do |t| t.string :title t.text :text t.timestamps end end end ستنشئ عملية التهجير أعلاه تابعًا يحمل اسم `change` والذي يتم استدعاؤه عند إجراء عملية التهجير. حتى الحدث المُعرّف ضمن التابع قابل للتراجع، ما يعني أن Rails قادر على التراجع عن التغييرات الحاصلة من إجراء عملية التهجير في حال أردت ذلك في وقت لاحق. عند إجراء عملية التهجير هذه سيتم إنشاء جدول باسم `articles` يتضمن عمودًا من نوع `string` وآخر من نوع `text`، إضافة إلى عمودين للختم الزمني يمكن لـ Rails من خلالهما متابعة تواريخ إنشاء وتعديل المقالات. لتنفيذ عمية التهجير توجّه إلى سطر الأوامر ونفذ الأمر التالي: $ bin/rails db:migrate سينفّذ Rails أمر التهجير التالي وسيخبرك بإنشاء جدول Articles. == CreateArticles: migrating ================================================== -- create_table(:articles) -> 0.0019s == CreateArticles: migrated (0.0020s) ========================================= حفظ البيانات بواسطة المتحكّم سنعود الآن إلى المتحكّم ArticlesController، حيث سنعمل على تعديل الحدث create ليستخدم النموذج الجديد Article لحفظ البيانات في قاعدة البيانات. افتح الملف app/controllers/articles_controller.rb وعدّله بالصورة التالية: def create @article = Article.new(params[:article]) @article.save redirect_to @article end يمكن استحداث initialize كل نموذج في Rails مع الخصائص Attributes المرتبطة به، والتي يتم ربطها تلقائيًا مع الأعمدة المقابلة في قاعدة البيانات. وقد قمنا بذلك في السطر الأول في الحدث create (هل تذكر params[:article] والذي يضمّ الخصائص التي نريدها). بعد ذلك يمكن حفظ النموذج في قاعدة البيانات من خلال الدالة @article.save. وفي النهاية نعيد توجيه المستخدم إلى الحدث show الذي سنعرّفه في وقت لاحق. توجّه الآن إلى العنوان http://localhost:3000/articles/new وستتلقّى الخطأ التالي: يدعم Rails العديد من مزايا الأمان التي تساعد في كتابة تطبيقات أمينة، ونحن الآن نتعامل مع إحدى هذه المزايا. تدعى هذه الميزة بالمعاملات القوية strong parameters والتي تجبرنا على تحديد المعاملات المسموح بها في الأحداث الموجودة ضمن المتحكم. ما الفائدة من ذلك؟ صحيح أن القدرة على إضافة جميع المعاملات إلى النموذج دفعة واحدة وبصورة تلقائية يختصر الكثير من الجهد بالنسبة للمبرمج، إلا أنّ البرنامج يكون في هذه الحالة عرضة للاستخدامات الخبيثة. فماذا لو تمّ إنشاء طلب إلى الخادوم يتضمن استمارة إنشاء مقالة جديدة إضافة إلى حقول أخرى تحتوي على معلومات تضرّ بالتطبيق؟ سيتم إسناد المعلومات الإضافية بصورة شاملة “Mass Assignment” إلى النموذج ثم إلى قاعدة البيانات جنبًا إلى جنب مع البيانات الأصلية، وهذا قد يتسبب في تعطيل عمل برنامجك أو قد يحدث ما هو أسوأ من ذلك بكثير. يجب علينا إذًا تحديد المعاملات المسموح بإدخالها إلى النموذج، وفي حالتنا هذه سنسمح بإدراج معاملي title و text ونطلب توفّر قيم لهما. وللقيام بذلك عدّل السطر الأول من حدث create بالصورة التالية: @article = Article.new(params.require(:article).permit(:title, :text)) غالبًا ما يتمّ تحديد المعاملات المسموح بإدخالها إلى النموذج في تابع خاص ليصبح بالإمكان إعادة استخدامها بواسطة عدة أحداث في المتحكّم نفسه مثل حدثي create و update، إضافة إلى ذلك يكون هذا التابع خاصًّا وذلك باستخدام المحدّد private لضمان عدم إمكانية استدعائه من خارج السياق المقرّر له، وبالشكل التالي: def create @article = Article.new(article_params) @article.save redirect_to @article end private def article_params params.require(:article).permit(:title, :text) end عرض المقالات إن قمت بتعبئة استمارة المقالة الجديدة وإرسالها فستتلقّى خطأ مفاده عدم عثور Rails على الحدث show، لذا سنقوم بإنشاء هذا الحدث الآن. كما رأينا سابقًا في مخرجات الأمر bin/rails routes فإن مسار الحدث show هو: article GET /articles/:id(.:format) articles#show تعني الصيغة الخاصة :id أن هذا المسار يطلب وجود معامل :id والذي يمثّل في حالتنا هذه معرّف المقالة. وكما فعلنا سابقًا، يجب علينا إضافة الحدث show إلى ملف المتحكّم app/controllers/articles_controller.rb وتحديد العرض المرتبط به. عادة ما تأخذ أحداث CRUD في المتحكّمات الترتيب التالي: index, show, new, edit, create, update, destroy. ويمكن اتّباع الترتيب الذي يعجبك، ولكن تذكّر أن هذه التوابع هي توابع عامّة public، ويجب الإعلان عنها قبل الإعلان عن التوابع الخاصّة. سنضيف الآن الحدث show آخذين ما سبق بعين الاعتبار: class ArticlesController < ApplicationController def show @article = Article.find(params[:id]) end def new end # بقيّة الشيفرة ..... استخدمنا الدالة Article.find للبحث عن المقالة المطلوبة، وذلك بتمرير المعامل params[:id] للحصول على قيمة المعرّف من الطلب الذي أرسلته صفحة إنشاء مقالة جديدة. كذلك استخدمنا متغيّرًا من نوع instance (مسبوقًا بعلامة @) ليكون مرجعًا لكائن المقالة، وذلك لأنّ Rails يمرّر هذا النوع من المتغيّرات إلى العرض. أنشئ الآن ملفًّا جديدًا باسم show.html.erb في المسار app/views/articles/ وأضف إليه الشيفرة التالية: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> ستكون الآن قادرًا على إنشاء مقالة جديدة؛ لذا توجّه إلى العنوان http://localhost:3000/articles/new وجرّب إضافة مقالة جديدة. عرض قائمة بمقالات المدوّنة نحتاج الآن إلى عرض قائمة بجميع المقالات الموجودة في المدونة، وسيكون المسار المرتبط بهذا الحدث وبحسب مخرجات الأمر bin/rails routes كالتالي: articles GET /articles(.:format) articles#index أضف الحدث index المرتبط بهذا المسار إلى المتحكّم ArticlesController في الملف app/controllers/articles_controller.rb. من الممارسات الشائعة بين المطوّرين هو كتابة الحدث index في بداية المتحكّم: class ArticlesController < ApplicationController def index @articles = Article.all end def show @article = Article.find(params[:id]) end def new end # بقية الشيفرة ... بعدها أضف العرض الخاصّ بهذا الحدث والموجود في المسار app/views/articles/index.html.erb والذي يتضمّن الشيفرة التالية: <h1>Listing articles</h1> <table> <tr> <th>Title</th> <th>Text</th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> </tr> <% end %> </table> توجّه الآن إلى العنوان http://localhost:3000/articles في المتصفّح وستشاهد قائمة بجميع المقالات التي أنشأتها مسبقًا. إضافة الروابط للتنقل بين صفحات المدوّنة أصبح بمقدورنا الآن إنشاء وعرض وسرد قائمة المقالات المتوفّرة في المدونة، ولكنّنا بحاجة إلى بعض الروابط التي تساعدنا في التنقل بين صفحات الموقع. افتح الملف app/views/welcome/index.html.erb وعدّله كما يلي: <h1>Hello, Rails!</h1> <%= link_to 'My Blog', controller: 'articles' %> التابع link_to هو أحد دوال العروض المساعدة والمضمّنة في Rails، ووظيفة هذا التابع إنشاء رابط تشعّبي بالاستناد إلى النصّ الذي نمرّره إليه، وهو في حالتنا هذه المسار الخاص بسرد قائمة المقالات. لنضف بعض الروابط إلى العروض الأخرى، ولنبدأ بإضافة رابط إنشاء مقالة جديدة إلى الملف app/views/articles/index.html.erb قبل وسم <table>: <%= link_to 'New article', new_article_path %> سيوجّه هذا الرابط المستخدم إلى الصفحة التي تتضمن استمارة إنشاء مقالة جديدة. سنضيف رابطًا آخر إلى الملفّ app/views/articles/new.html.erb بعد الاستمارة مباشرة، ليتمكن المستخدم من العودة إلى الصفحة الرئيسية: <%= form_for :article, url: articles_path do |f| %> ... <% end %> <%= link_to 'Back', articles_path %> وأخيرًا، سنضيف رابطًا إلى القالب app/views/articles/show.html.erb يوجّه المستخدم إلى الصفحة الرئيسية أيضًا، وبهذا يصبح بميسور من يستعرض مقالة معيّنة أن يرجع إلى الصفحة التي تعرض جميع المقالات: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <%= link_to 'Back', articles_path %> التحقّق من المدخلات لو نظرت إلى النموذج الذي أنشأناه سابقًا فسترى أنّ الملف بسيطٌ للغاية: class Article < ApplicationRecord end لاحظ أنّ الصنف Article موروث من الصنف ApplicationRecord وهو بدوره موروث من الصنف ActiveRecord::Base والذي يتضمّن الكثير من الوظائف والإجراءات الخاصة بالنماذج، مثل عمليات CRUD البسيطة (Create, Read, Update, Destroy) والتحقّق من البيانات Validation، إضافة إلى عمليات البحث المعقّدة وربط النماذج المختلفة مع بعضها البعض. ويقدّم إطار العمل Rails توابع متعدّدة تساعد في التحقق من البيانات المرسلة إلى النموذج. افتح الملف app/models/article.rb وأضف إليه الشيفرة التالية: class Article < ApplicationRecord validates :title, presence: true, length: { minimum: 5 } end سيضمن هذا التغيير امتلاك كل مقالة جديدة في المدونة لعنوان يتألف من خمسة أحرف على الأقل. يتيح Rails التحقّق من أمور متنوّعة في النماذج، مثل التحقّق من وجود أو عدم تكرار الأعمدة والتحقّق من تنسيقها ووجود كائنات مرتبطة بها. لنجرّب الآن استدعاء الدالة @article.save في مقالة لا تمتلك عنوانًا وسنلاحظ أن الدالة ترجع القيمة false. لو عدنا إلى المتحكّم في الملف app/controllers/articles_controller.rb مرّة أخرى سنلاحظ بأنّنا لم نتحقّق من النتيجة التي ترجعها الدالة @article.save ضمن الحدث create. إن فشلت الدالة @article.save في أداء عملها، يجب أن نعيد المستخدم إلى استمارة إضافة مقالة جديدة، وللقيام بذلك عدّل حدثي new و create في الملف app/controllers/articles_controller.rb بالصورة التالية: def new @article = Article.new end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end private def article_params params.require(:article).permit(:title, :text) end سينشئ الحدث new متغيّرًا جديدًا من نوع instance يحمل الاسم @article وستتعرّف إلى سبب القيام بذلك بعد قليل. لاحظ أنّنا استخدمنا render داخل الحدث create بدلًا من redirect_to في حال إرجاع الدالة save للقيمة false. يستخدم التابع render لكي يتم تمرير الكائن @article إلى القالب الجديد عند تصييره. وعملية التصيير هذه تتم ضمن نفس الطلب الناتج من إرسال الاستمارة، في حين أن الدالة redirect_to تتسبّب في إرسال طلب آخر. الآن أعد تحميل الصفحة ذات العنوان http://localhost:3000/articles/new وحاول إضافة مقالة جديد دون عنوان، سترى بأنّ Rails يعيدك إلى صفحة الاستمارة، ولكن هذا ليس مفيدًا جدًّا. يجب إخبار المستخدم بحدوث خطأ ما، وللقيام بذلك عدّل الملف app/views/articles/new.html.erb للتحقّق من رسائل الخطأ: <%= form_for :article, url: articles_path do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', articles_path %> تحقّقنا في البداية من وجود أي أخطاء من خلال @article.errors.any?، وفي حال وجودها نعرض قائمة الأخطاء المتوفّرة من خلال @article.errors.full_messages. تأخذ الدالة pluralize معاملين الأول رقمي والثاني نصّي. إن كان العدد أكبر من واحد تتحوّل السلسلة النصّية تلقائيًا إلى صيغة الجمع. إن سبب إضافة @article = Article.new إلى المتحكّم ArticlesController هو أنّنا لو لم نقم بذلك لأصبحت قيمة المتغيّر @articl هي nil، وسيؤدي الاستدعاء @article.errores.any? إلى إطلاق خطأ. يحيط Rails الحقول التي تحتوي على أخطاء بوسم <div> مع صنف CSS يحمل الاسم field_with_errors، ويمكنك تعريف صنف CSS هذا لتنسيق الحقول حسب الرغبة. والآن ستتلقّى رسالة خطا مرتّبة عندما تحاول حفظ مقالة لا تتضمن عنوانًا. في الدرس القادم سنواصل العمل على النموذج حيث سنكتب الشيفرة المسؤولة عن تعديل المقالات وحذفها، ثم سنتعرّف على طريقة إنشاء علاقات بين النماذج المختلفة من خلال إضافة نموذج للتعامل مع التعليقات في المدونة. المصدر: توثيقات Ruby on Rails.
  6. تعرّفنا في الدرس السابق على طريقة تثبيت إطار العمل Rails وبدأنا العمل على مشروعنا الأول وهو عبارة عن مدوّنة بسيطة، وقمنا بتشغيل الخادوم الخاص بإطار العمل. وفي هذا الدرس سنتعرّف على آلية عمل إطار العمل Rails من خلال مثال بسيط، ثم نشرع بعده ببناء مدونتنا البسيطة لنتعرف بصورة أكبر على العديد من المفاهيم التي يستند إليها هذا الإطار. آلية عمل إطار Rails سنتعرّف على آلية عمل إطار Ruby on Rails من خلال مثال بسيط نعرض فيه مجموعة من الكلمات في الصفحة الرئيسية لتطبيقنا، وللقيام بذلك سنحتاج إلى متحكّم Controller وعرض View. وظيفة المتحكّم هي استقبال الطلبات الواردة إلى التطبيق، وتربط المسارات Routes بين الطلبات والمتحكّمات. وغالبًا ما يكون هناك أكثر من مسار واحد لكل متحكّم، ويمكن للمسارات المختلفة أن تؤدّي إلى أحداث Actions مختلفة، ووظيفة الحدث هي جمع المعلومات اللازمة وتقديمها إلى العرض. أمّا وظيفة العرض فواضحة من اسمه، وهي عرض المعلومات التي حصل عليها من الحدث بصورة مقروءة للإنسان. من الضروري هنا الانتباه إلى أن عملية جمع المعلومات تتمّ ضمن المتحكّم وليس ضمن العرض، ومهمّة العرض الوحيدة هي عرض المعلومات. يستخدم إطار Rails لغة قوالب خاصّة في العروض تدعى eRuby (اختصار لـ Embedded Ruby) والتي تُعالج بواسطة دورة الطلب في Rails قبل أن تُرسل إلى المستخدم. سلنجأ إلى أداة المولّد generator لإنشاء متحكّم يحمل اسم Welcome يتضمّن حدثًا باسم index. اكتب الأمر التالي في سطر الأوامر: $ bin/rails generate controller Welcome index سيقوم Rails بإنشاء مسار وعدد من الملفات. create app/controllers/welcome_controller.rb route get 'welcome/index' invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke test_unit create test/controllers/welcome_controller_test.rb invoke helper create app/helpers/welcome_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/welcome.coffee invoke scss create app/assets/stylesheets/welcome.scss ما يهمّنا من هذه الملفات هما المتحكم والموجود في المسار app/controllers/welcome_controller.rb والعرض الموجود في المسار app/views/welcome/index.html.erb. افتح الملف app/views/welcome/index.html.erb في محرّر النصوص المفضّل لديك، واحذف محتوياته واستبدلها بالشيفرة التالية: <h1>Hello, Rails!</h1> بعد أن أنشئنا المتحكم والعرض يجب علينا إخبار Rails بالمسار الذي سيأخذ المستخدم إلى هذا العرض. نحن نرغب في حالتنا هذه أن يتم توجيه المستخدم إلى العرض عندما يتوجّه إلى العنوان http://localhost:3000، ولكن صفحة الترحيب تشغل هذا المسار في الوقت الحاضر. بعد ذلك يجب إخبار Rails بموقع الصفحة الرئيسية ليتمكّن من عرضها للمستخدم. افتح الملف config/routes.rb في محرّر النصوص: Rails.application.routes.draw do get 'welcome/index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end الشيفرة أعلاه موجودة في ملف المسارات والذي يتضمّن مدخلات DSL خاصّة (DSL اختصار لـ domain-specific language) والتي تخبر Rail بطريقة ربط الطلبات الواردة إلى التطبيق بالمتحكمات والأحداث. عدّل هذا الملف بالصورة التالية: Rails.application.routes.draw do get 'welcome/index' root 'welcome#index' end من خلال السطر root 'welcome#index' يربط إطار العمل Rails الطلبات الواردة إلى المسار الرئيسي في التطبيق مع الحدث index في المتحكّم Welcome، أما السطر get 'welcome/index' فيربط من خلاله Rails الطلبات الواردة إلى العنوان http://localhost:3000/welcome/index بنفس الحدث ونفس المتحكّم، وقد تمّ إنشاء هذه الشيفرة من قبل أداة المولّد. والآن شغّل الخادوم الخاص بـ Rails ثمّ توجّه في المتصفّح إلى العنوان http://localhost:3000، وستشاهد عبارة “Hello, Rails!” الموجودة في ملف app/views/welcome/index.html.erb وهذا يعني أن هذا المسار قد توجّه فعلًا إلى الحدث index في المتحكم Welcome والذي قام بدوره بتصيير العرض بصورة صحيحة. البدء بإنشاء المدوّنة بعد أن تعرّفنا على المتحكّمات والأحداث والعروض، لنبدأ العمل على مدوّنتنا. سننشئ الآن ما يسمى في إطار العمل Rail بالمورد resourse، والمورد هو مصطلح يعبّر عن مجموعة من العناصر المتشابهة، مثل المقالات، الأشخاص أو الحيوانات. ويمكن إنشاء create وقراءة read وتحديث update وإلغاء destroy العناصر في المورد، وتسمى هذه العمليات بعمليات CRUD. يقدّم Rails تابعًا باسم resources يمكن استخدامه للإفصاح عن مورد بنمط REST القياسي. يجب إضافة مورد المقالة إلى ملف config/routes.rb وكما يلي: Rails.application.routes.draw do get 'welcome/index' resources :articles root 'welcome#index' end والآن إن قمت بتنفيذ الأمر bin/rails routes فستشاهد جميع المسارات الخاصّة بجميع الأحداث التي تتّصف بنمط REST. سنتعرّف على معنى عمود prefix وبقية الأعمدة في وقت لاحق، ولكن لاحظ أنّ Rails قد خمّن صيغة المفرد (article) واستخدمها في السياق الصحيح. $ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update DELETE /articles/:id(.:format) articles#destroy root GET / welcome#index سنعمل الآن على إضافة خاصيتي إنشاء المقالات وعرضها، وستكون الاستمارة Form المسؤولة عن ذلك بالشكل التالي: قد تبدو الاستمارة بدائيًّة ولكنّها كافية في الوقت الحاضر، وسنعمل على تحسين مظهرها فيما بعد. إنشاء المتحكّمات والمسارات اللازمة في البداية يجب اختيار المسار الذي سيوجّه المستخدم إلى استمارة إنشاء المقالة الجديدة، وسنستخدم المسار /articles/new للقيام بهذه المهمّة، بعدها يصبح بميسور التطبيق أن يتلقّى الطلبات على هذا المسار. توجّه الآن في متصفحك إلى الرابط http://localhost:3000/articles/new وستتلقّى الخطأ التالي: يحدث هذا الخطأ لأنّ المسار بحاجة إلى متحكّم يرسل إليه الطلب؛ لذا سنقوم بإنشاء متحكّم باسم ArticlesController، وذلك من خلال تنفيذ الأمر التالي: $ bin/rails generate controller Articles افتح الملف الذي قمت بإنشائه app/controllers/articles_controller.rb وسترى متحكّمًا فارغًا: class ArticlesController < ApplicationController end المتحكّم عبارة عن صنف Class موروث من ApplicationController وسنقوم بتعريف التوابع ضمن هذا الصنف والتي ستمثل الأحداث الخاصّة بهذا المتحكم، وهذه الأحداث هي المسؤولة عن تنفيذ عمليات CRUD على المقالات الموجودة في تطبيقنا. إن أعدت تحميل الصفحة ستتلقّى خطأً جديدًا: يشير هذا الخطأ إلى عدم قدرة Rails على إيجاد الحدث new ضمن المتحكّم ArticlesController الذي قمنا بإنشائه للتوّ. وهذا عائد إلى أنّ المتحكّمات تكون فارغة عند إنشائها إلا إذا حدّدنا الأحداث المطلوبة خلال عملية إنشاء المتحكّم. ولتعريف حدث جديد بصورة يدوية، سنحتاج فقط إلى تعريف تابع جديد ضمن المتحكم. افتح الملف app/controllers/articles_controller.rb وضمن الصنف ArticlesController عرّف تابعًا جديدًا وكما يلي: class ArticlesController < ApplicationController def new end end والآن أعد تحميل الصفحة في المتصفّح وستتلقّى خطأً آخر: يظهر هذا الخطأ لأنّ Rails يتوقّع أنه يجب أن تمتلك الأحداث الصرفة المشابهة لهذا الحدث عروضًا ترتبط معها لعرض المعلومات التي تتضمنها، ونظرًا لعدم وجود أي عرض مرتبط بهذا الحدث، أطلق Rails هذا الخطأ. لنطّلع على رسالة الخطأ الكاملة: لنستعرض النص السابق سريعًا، ونفهم مضمونه بصورة جيدة. يحدّد الجزء الأول من نصّ الخطأ القالب المفقود، وفي هذه الحالة القالب المفقود هو articles/new. يبدأ Rails بالبحث عن هذا القالب، وإن لم يفلح في العثور عليه فإنه يحاول تحميل قالب يدعى application/new وذلك لأنّ المتحكّم ArticleController هو صنف موروث من المتحكّم ApplicationController. يتضمن الجزء الثاني من رسالة الخطأ request.formats والذي يحدّد صيغة القالب الذي سيتم عرضه كاستجابة للطلب الذي تلقّاه التطبيق، وقد تم اختيار صيغة text/html لأنّنا طلبنا هذه الصفحة بواسطة المتصفح؛ لذا يبحث Rails عن قوالب HTML. أما request.variant فيحدد طبيعة الأدوات المادّية physical devices التي سيتم تقديمها مع الطلب وتساعد Rails في تحديد القالب الذي سيستخدمه في الاستجابة، وهو فارغ نظرًا لعدم توفّر المعلومات. أبسط قالب يمكن أن يعمل في هذه الحالة هو القالب الموجود في المسار app/views/articles/new.html.erb. هذه اللاحقة مهمّة للغاية: فالجزء الأول من اللاحقة (.html) يعبّر عن صيغة القالب، أمّا الجزء الثاني (.erb) فيمثّل المعالج handler الذي سيتم استخدامه في تصيير القالب. يحاول Rails البحث عن قالب يحمل الاسم articles/new ضمن المجلد app/views. يجب أن تكون صيغة هذا القالب هي HTML حصرًا، وسيكون erb المعالج الافتراضي لـ HTML. يستخدم Rails عددًا من المعالجات مثل: builder والمستخدم في إنشاء قوالب XML، وcoffee الذي يستخدم لغة CoffeeScript لبناء قوالب JavaScript. بما أنّنا نرغب في بناء استمارة HTML جديدة فسنستخدم لغة ERB والتي تتيح لنا تضمين لغة Ruby في HTML. إذًا سيكون اسم القالب articles/new.html.erb وسيكون ضمن المجلد app/views الخاص بالتطبيق. أنشئ ملفًّا جديدًا باسم new.html.erb في المسار app/views/articles وأضف إليه ما يلي: <h1>New Article</h1> أعد تحميل الصفحة وستلاحظ ظهور العنوان في رأس الصفحة، وهذا يعني أن هناك تناغمًا تامًّا بين كلّ من المسار والمتحكم والحدث والعرض. الاستمارة الأولى سنستخدم منشئ النماذج form builder لإنشاء الاستمارة الأولى في هذا القالب. يمكن استخدام منشئ النماذج الرئيسي في Rails باستخدام التابع المساعد form_for. أضف الشيفرة التالية في الملف app/views/articles/new.html.erb: <%= form_for :article do |f| %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> أعد تحميل الصفحة وستلاحظ ظهور نفس الاستمارة التي عرضناها في المثال السابق. كما تلاحظ فإنّ بناء الاستمارات في Rails أمر سهلٌ للغاية. عندما نستدعي التابع form_for فإننا نمرّر إليه عنصرًا يحدّد الهدف من هذه الاستمارة، والهدف في حالتنا هذه هو :article. يُستخدم الكائن FormBuilder والذي مثّلناه بـ f لبناء عنصري label وحقلي نصوص text fields لكلّ من عنوان المقال ومتنها. وفي النهاية استدعينا التابع submit لإنشاء زرّ اﻹرسال الخاصّ بالاستمارة. ولكن تعاني هذه الاستمارة من مشكلة صغيرة. لو تفحّصت شيفرة HTML التي تم توليدها من خلال الشيفرة السابقة فستلاحظ أن خاصية action التابعة للاستمارة تشير إلى المسار articles/new وهذا المسار هو نفسه الذي يقودنا إلى هذه الصفحة، والمفروض أن يستخدم هذا المسار لعرض استمارة إنشاء مقالة جديدة لا غير. إذًا يجب أن تستخدم الاستمارة مسارًا آخر، ويمكن القيام بذلك بسهولة من خلال استخدام الخيار :url في form_for. عادة ما يحمل الحدث المسؤول عن إرسال مقال جديد اسم “create”، لذا يجب توجيه الاستمارة إلى هذا الحدث. عدّل السطر الذي يتضمن form_for في ملف app/views/articles/new.html.erb كما يلي: <%= form_for :article, url: articles_path do |f| %> في هذا المثال تم تمرير الدالة المساعدة articles_path إلى الخيار :url، ولنتعرّف على نتيجة هذا التعديل سنلقي نظرة على مخرجات الأمر bin/rails routes في سطر اﻷوامر: $ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update DELETE /articles/:id(.:format) articles#destroy root GET / welcome#index توجّه الدالة المساعدة articles_pat الاستمارة إلى نمط URI المرتبط لاحقة articles وسيرسل الاستمارة - تلقائيًا - طلبًا من نوع POST إلى المسار، والذي يرتبط بالحدث create التابع للمتحكّم ArticlesController. بعد أن أنشأنا الاستمارة وعرّفنا المسار المرتبط به، أصبح باﻹمكان تعبئة حقول الاستمارة والضغط على زرّ اﻹرسال لبدء عملية إنشاء مقال جديد، ولكن عند إرسال المقال ستتلقّى الخطأ المتوقَّع التالي: علاج هذا الخطأ بسيط وهو إنشاء الحدث create ضمن المتحكّم ArticlesController. إنشاء المقالات سنقوم الآن بتعريف الحدث create ضمن صنف ArticlesController في الملف app/controllers/articles_controller.rb بعد الحدث new وكما يلي: class ArticlesController < ApplicationController def new end def create end end إن أعدت إرسال الاستمارة مرة أخرى فستلاحظ عدم حدوث أي تغيير في الصفحة. لا تقلق، هذا الأمر عائد إلى أنّ Rails يعيد الاستجابة “204 No Content” لأي حدث لا يحدّد الاستجابة المطلوبة. وقد أضفنا الحدث create دون تحديد الاستجابة المطلوبة منه، وهي في حالتنا هذه، إنشاء مقالة جديدة في قاعدة البيانات. عند إرسال الاستمارة يتم إرسال الحقول الخاصة بها إلى Rails على هيئة معاملات parameters، يمكن الإشارة إليها في الأحداث التابعة للمتحكّم وذلك لإنجاز مهّمة ما، ولتعرف كيف تبدو هذه المعاملات عدّل حدث create بالصورة التالية: def create render plain: params[:article].inspect end يأخذ التابع render هنا جدول تقطيع Hash بسيط مع المفتاح :plain والقيمة هي تابع params [:article].inspect. توابع params هي الكائن الذي يمثّل المعامل (أو الحقل) المأخوذ من الاستمارة. ويعيد تابع params كائن من نوع ActionController::Parameters والذي يتيح لنا الوصول إلى مفاتيح جدول التقطيع من خلال السلاسل النصّيّة Strings أو الرموز Symbols. وفي حالتنا هذه، فإن المعاملات المهمّة هي المعاملات المأخوذة من الاستمارة. لتوضيح عمل توابع params، إليك المثال التالي: في عنوان URL هذا: http://www.example.com/?username=dhh&email=dhh@mail.com فإن params[:username] تحمل القيمة “dhh” و params[:email] تحمل القيمة “dhh@mail.com”. والآن أعد إرسال الاستمارة مرة أخرى وستشاهد شيئًا مماثلًا لما يلي: <ActionController::Parameters {"title"=>"First Article!", "text"=>"This is my first article."} permitted: false> يعرض هذا الحدث المعاملات الخاصة بالمقالة والمأخوذة من الاستمارة، ولكن ليس هذا ما نريده بالضبط، فنحن نشاهد المعاملات ولكنّها لا تقدّم أي فائدة تذكر في حالتها هذه. في الدرس القادم سنتعرّف على النماذج Models في إطار العمل Rails وسنستخدمها في إضافة مقالة جديدة إلى قاعدة البيانات التابعة للتطبيق. المصدر: توثيقات Ruby on Rails.
  7. Rails هو إطار عمل لتطوير تطبيقات الويب مكتوب بلغة Ruby البرمجية، وقد صُمّم إطار العمل هذا لتسهيل برمجة تطبيقات الويب من خلال وضع بعض الافتراضات المسبقة حول ما يحتاجه المطوّر للشروع في العمل. يتيح إطار العمل هذا كتابة شيفرات أقل وإنجاز أمور أكثر من أي لغة برمجية أو أطر عمل أخرى. يفترض Rails أن هناك طريقة مثلى لإنجاز الأعمال، وقد صمّم لتشجيع المطوّر على اتباع هذه الطرق وفي بعض الأحيان حثّه على ترك البدائل الأخرى، وقد تلاحظ زيادة هائلة في إنتاجيتك إن تعلّمت الأسلوب الذي يتبعه إطار العمل هذا، وقد يؤدي الالتزام بالعادات القديمة المتّبعة في لغات البرمجة الأخرى إلى تجربة غير جيّدة في تطوير التطبيقات باستخدام Rails. تستند فلسفة Rails إلى ركيزتين أساسيتين: لا تكرّر نفسك (Don’t Repeat Yourself): ينصّ هذا المفهوم على وجوب تمثيل أي جزء من أجزاء المعرفة بصورة مفردة وغير مبهمة وموثوقة في النظام. اتّباع هذا المفهوم في كتابة النصوص البرمجية وعدم تكرار المعلومات ذاتها باستمرار يؤدي إلى زيادة قابلية صيانة الشيفرة المكتوبة وامتلاكها القدرة على التوسّع إضافة إلى انخفاض نسبة الأخطاء فيها. مبدأ “Convention Over Configuration”: يمتلك Rails مبادئ خاصّة ترتبط بتحديد الطريقة المثلى في إنجاز الأعمال في تطبيق الويب، ويعتمد على هذه المبادئ بصورة افتراضية بدلًا من إجبار المطوّر على تحديد تفاصيل صغيرة في عمله من خلال عدد كبير من اﻹعدادات. ما الذي تحتاجه للبدء هذه السلسلة مخصصة للمبتدئين الذين يرغبون في الشروع ببناء التطبيقات على إطار العمل Rails، ولا يفترض وجود أي خبرة سابقة في هذا المجال، ولكن هناك بعض الأمور التي يجب تثبيتها قبل الشروع في التعلم: تثبيت الإصدار 2.2.2 من لغة Ruby البرمجية أو أي إصدار أعلى. تثبيت النسخة الملائمة من حزمة التطوير Development Kit إن كنت من مستخدمي نظام التشغيل ويندوز. نظام إدارة الحزم (الجواهر) RubyGems والذي يأتي مع لغة Ruby بصورة افتراضية. تثبيت قواعد بيانات SQLite3. ذكرنا أنّ إطار العمل Rails مبني باستخدام لغة Ruby البرمجية، وإن كنت لا تمتلك خبرة مسبقة بهذه اللغة يمكنك مراجعة الدروس المتوفّرة حول أساسيات لغة Ruby في الأكاديمية إضافة إلى الموقع الرسمي للغة. إنشاء مشروع جديد في Rails الهدف من هذه السلسلة هو بناء مدوّنة بسيطة باستخدام إطار العمل Rails، وقبل البدء في بناء التطبيق يجب التأكد من تثبيت Rails في جهازك. سنستخدم الرمز $ للتعبير عن سطر الأوامر في الأنظمة الشبيهة بـ UNIX. أما في نظام ويندوز فسترى سطر الأوامر يبدأ بشيء مشابه لهذه الصيغة: <C:\source_code. تثبيت Rails توجّه إلى سطر الأوامر في جهازك (في نظام macOS افتح الطرفية Terminal.app، وفي نظام ويندوز اختر “Run” من قائمة ابدأ ثم اكتب cmd.exe). في البداية سنتأكد من إصدار لغة Ruby المثبت في الجهاز: $ ruby -v ruby 2.3.1p112 بالنسبة لقواعد البيانات SQLite3 فعادة ما تكون مثبّتة بشكل افتراضي في الأنظمة الشبيهة بـ UNIX. أما في نظام ويندوز، فإن قمت بتثبيت Rails من خلال مثبت Rails فإن SQLite ستكون مثبتة على جهازك أيضاً. يمكنك كذلك مراجعة موقع SQLite3 للاطلاع على تعليمات تثبيت قاعدة البيانات. يمكن التحقق من سلامة تثبيت SQLite3 من خلال الأمر التالي: $ sqlite3 --version إن كانت SQLite مثبتة في الجهاز فسيظهر رقم الإصدار المثبت في سطر الأوامر. لتثبيت Rails استخدم أمر التثبيت gem install الذي يتيحه RubyGems وبالصورة التالية: $ gem install rails وللتأكد من أن عملية التثبيت قد تمّت بصورة صحيحة، يجب أن تكون قادرًا على تنفيذ الأمر التالي في سطر الأوامر: $ rails --version يجب أن تحصل على نتيجة مشابهة لهذه: Rails 5.1.0. إنشاء تطبيق المدوّنة يقدّم إطار العمل Rails مجموعة من الشيفرات تحمل اسم المولّدات generators، وتهدف هذه الشيفرات إلى تسهيل عمل المطوّر من خلال إنشاء الملفات المطلوبة للشروع في مهمّة معيّنة. مولّد التطبيق الجديد هو أحد هذه المولّدات ويعمل على إنشاء تطبيق Rails جديد وتوفير عناء كتابته من قبل المطور. ولاستخدام المولّد توجّه في سطر الأوامر إلى المجلد الذي ترغب في إنشاء التطبيق فيه واكتب الأمر التالي: $ rails new blog سينشئ هذا الأمر تطبيقًا جديدًا باسم Blog في مجلد blog وسيثبت اعتماديات gem الموجودة في GEMfile باستخدام الأمر bundle install. يمكنك الاطلاع على جميع الخيارات المتاحة في سطر الأوامر والتي يتقبّلها مولّد تطبيقات Rails وذلك من خلال تنفيذ الأمر: rails new -h بعد إنشاء تطبيق المدوّنة، توجّه في سطر الأوامر إلى المجلّد الخاص به: $ cd blog ستلاحظ أنّ مجلّد المدونة يتضمن بعض الملفات والمجلّدات التي تم إنشاؤها بصورة تلقائية والتي تمثّل العمود الفقري لتطبيق Rails. سينحصر الجزء الأكبر من عملنا ضمن مجلد app، ولكن لا بأس في الاطلاع بصورة سريعة على وظيفة هذه الملفات والمجلّدات: الملف أو المجلد الوظيفة /app يتضمن هذا المجلّد: المتحكّمات controllers، النماذج models، العروض views، الدوال المساعدة helpers، دوال البريد اﻹلكتروني mailers، القنوات channels، الوظائف jobs، والأصول assets الخاصّة بالتطبيق. سيتركّز عملنا ضمن هذا المجلد. /bin يتضمّن هذا المجلد شيفرات Rails المسؤولة عن تشغيل التطبيق ويمكن أن يتضمن شيفرات أخرى تستخدم في تثبيت وتحديث ونشر وتشغيل التطبيق. /config يتضمن الإعدادات الخاصة بمسارات التطبيق routes، وقاعدة البيانات وغير ذلك config.ru ملف إعدادات Rack يستخدم في خواديم Rack لتشغيل التطبيق عليها. /db يتضمّن مخطط قاعدة البيانات الحالية، إضافة إلى تهجيرات قاعدة البيانات. Gemfile, Gemfile.lock يتيح هذان الملفان تحديد اعتماديات gem المطلوبة لتطبيق Rails. يستخدم Bundler هذه الملفات. لمزيد من المعلومات توجّه إلى موقع Bundler الإلكتروني. /lib الوحدات الموسّعة الخاصة بالتطبيق. /log ملفات log الخاصة بالتطبيق. /public المجلد الوحيد الذي سيظهر على حاله بعد نشر التطبيق، ويتضمن الملفات الساكنة و ملفات الأصول المجمّعة. Rakefile يحدّد هذا الملف ويحمّل المهامّ التي يمكن تنفيذها بواسطة سطر الأوامر. يتم تعريف المهامّ ضمن مكوّنات Rails. ولإضافة مهامّ جديدة يجب عدم تعديل هذا الملف، بل إضافة ملفات إلى مجلّد lig/tasks. README.md الملفّ التعريفي الخاصّ بالتطبيق، ويمكن من خلاله تقديم نبذة تعريفية عن التطبيق والمهام التي يؤديها وطريقة التثبيت وغير ذلك من المعلومات. /test يضمّ هذا المجلد جميع الأمور المرتبطة بالاختبارات. /tmp يضمّ هذا المجلّد الملفّات المؤقتة (مثل ملفات الذاكرة المخبئية وملفات pid). /vendor ستجد هنا جميع شيفرات الطرف الثالث، وعادة ما يتضمن جواهر مطوّرة من قبل أشخاص أو شركات. gitignore. يخبر هذا الملف نظام التحكم في النسخ Git عن الملفات أو (الأنماط) التي ينبغي عليه تجاهلها. لتعرف المزيد راجع سلسلة دروس Git في الأكاديمية. مرحبًا Rails لنحاول في البداية إظهار بعض النصوص على الشاشة وبصورة سريعة، وللقيام بذلك، ستحتاج إلى تشغيل الخادوم الخاص بـ Rails. تشغيل خادوم Rails يتضمّن إطار العمل Rails خادوم ويب خاصًّا به وكل ما نحتاج إليه هو تشغيله وذلك من خلال تنفيذ الأمر التالي في سطر الأوامر ضمن مجلد blog: $ bin/rails server إن كنت تستخدم نظام ويندوز يجب تمرير الشيفرات في مجلد bin إلى مفسّر Ruby مباشرة: ruby bin\rails server تنفيذ هذا الأمر سيؤدي إلى تشغيل Puma، وهو خادوم ويب مضمّن بصورة افتراضية في إطار العمل Rails. حان الآن وقت الولوج إلى تطبيقنا من خلال المتصفح وذلك بالتوجه إلى الرابط http://localhost:3000/. ستظهر الصفحة التالية لتشير إلى نجاحنا في إنشاء أول مشروع على Ruby on Rails. المصدر: توثيقات Ruby on Rails.
  8. يتعامل المبرمجون مع شفرة البرامج المصدرية بأسلوبين مختلفين، فالأول يرى أنّ “الشفرة التي تعمل هي الشفرة الجيدة” أما الثاني فيرى أنّه “ما دامت الشفرة جيدة فإنّها ستعمل بكل تأكيد” وبصياغة أخرى: “المهمّ أنها تعمل” مقابل “المهمّ أن تكون صحيحة”. كل يوم تقريبًا أقرأ هذه العبارة وأشباهها في تعليقات المدونة: “ما الحاجة إلى مبادئ البرمجة كائنية التوجه إن كانت الشفرة البرمجية تعمل بشكل جيّد دون استخدامها؟ ما الهدف من إدخال طرق وأساليب جديدة يفترض بها أن تكون أفضل من سابقاتها، إن كانت الطريقة التقليدية الحالية - والتي تتوسط البرمجة الإجرائية وكائنية التوجه - تعمل جيّدًا؟” لنفكّر في الأمر من زاوية مختلفة، وننظر إلى الأمور على نحو أعمّ ونفكّر من ناحية تطوير البرمجيات لا من ناحية البرمجة كائنية التوجه. هناك الكثير من الأمثلة التي تنطبق عليها عقلية “المهمّ أنّه يعمل”. لنأخذ لغة Perl، وهي لغة برمجية تشتهر بقدرتها على القيام بأيّ شيء بثلاث طرق مختلفة، بمعنى أنّه لا وجود لطريقة واحدة صحيحة. لست خبيرًا في Perl، لذا فلنلق نظرة على شفرة Ruby التالية: if a > b m = 'Hello!' end يمكن كتابة الشفرة السابقة بهذه الطريقة أيضًا: m = if a > b 'Hello!' end أو هذه: m = 'Hello!' if a > b وإليك المزيد: m = a > b ? 'Hello' : nil أيّ الشفرات السابقة صحيحة؟ هل يمكن ﻷي مبرمج بلغة Perl أن يخبرنا بذلك؟ هل يمكن اقتراح طرق أخرى للوصول إلى نفس النتيجة؟ أما في لغة Java (وهي لغة أكثر صرامة من Ruby) فليس من المفاجئ أن تكون هناك طريقة واحدة للقيام بذلك: if (a > b) { m = "Hello!"; } أعتقد أنّني أخطأت، فهنالك طريقة ثانية: if (a > b) m = "Hello!"; ما الذي يمكن أن يجنيه المبرمجون من هذا التنوّع الكبير؟ أعتقد أن الإجابة تعتمد على كوننا نكتب الشفرة أم نقرؤها؟ كذلك يعتمد الأمر على موقفنا تجاه البرنامج الذي نعمل على إنشائه، فإما نرى بأنّه ملكنا (عقلية المخترق) أو أنّنا نبنيه وحسب (عقلية المصمّم). إن كنا نكتب الشفرة البرمجية، وكنا نرى أنفسنا أصحاب تلك الشفرة، فسنحتاج إلى ترسانة أسلحة التجميل اللغوي Syntactic sugar لنثبت لأنفسنا بأننا أذكياء، وبالتأكيد لنتباهى أمام أصدقائنا بمفسّر Ruby الكئيب. في المقابل، إن كنا نعدّ أنفسنا في عداد المصممين، فسنصاب بالانزعاج والإحباط عند قراءة شفرة برمجية مليئة بأساليب التجميل اللغوي والتي “تعمل دون مشاكل”. ربما يجب أن أقتصر في الحديث على نفسي، الواقع أنني متأكّد من أنني سأصاب بهذا الشعور. يمكن أن نعدّ صياغة لغة Ruby والتي تتيح هذا القدر الكبير من التجميل اللغوي مثالًا واضحًا على التناقض الحاصل بين مبدأي “المهمّ أن تعمل” و “المهم أن تكون جيدة”. إذ تتمثل فلسفة Ruby في أنّه لا أهمّية لطريقة كتابة الشفرة البرمجية ما دامت تؤدّي عملها المطلوب منها. أما فلسفة Java فمختلفة تمامًا، وهي أقرب ما تكون إلى: اكتب شفرة صحيحة وستعمل بالتأكيد. إضافة إلى ذلك، فإن نوع البيانات الضعيف والديناميكي Weak and dynamic type الذي تتمتع به لغة Ruby مقابل نوع البيانات الصلب والثابت Strong and static type في Java، يعدّ دليلًا آخر عمّا أتحدّث عنه. أرى عموما أنّه كلما زادت مرونة اللغة البرمجية، قلّت قابلية صيانة الشفرة المكتوبة بها، وبمعنى آخر فإن الجودة الأعلى تأتي من اللغات الأبسط. وهذا الأمر ينطبق كذلك على عملية تطوير البرمجيات: فكلما زادت القيود المفروضة على المبرمجين وقلّت الخيارات المتاحة أمامهم في طريقة كتابة الشفرة البرمجية، زادت جودة البرنامج المكتوب بتلك اللغة. تحاول المحلّلات الساكنة Static analyzers مثل Checkstyle في لغة Java أو Rubocop في لغة Ruby حلّ هذه المشكلة وذلك بمنعنا من استخدام خصائص معيّنة في اللغة البرمجية، ولكنّها لا تقدّم نتائج جيّدة لأنّنا مبدعون في كتابة الشفرات بأساليب وأشكال متنوعة. لنعد الآن إلى السؤال الخاص بالبرمجة كائنية التوجه: ما الحاجة إلى تطوير أي شيء إن كان يؤدي عمله بهيئته الحالية؟ الجواب: البرمجة كائنية التوجه الحديثة (كما في Java، Ruby و ++C) لا تنتج شفرة برمجية ذات جودة عالية لأنّها لا تتبع تصوّرا برمجيًّا Paradigm قويًا ومقيّدًا بشكل ملائم. كل ما في الأمر أنّها توفّر الكثير من “الميزات” التي أدخلتها في الغالب ++C وبقيت فيها لأجل منفعتنا المتبادلة. هذه اللغات تعمل بالفعل، ولكنّ قابلية صيانة البرامج التي ننتجها بهذه اللغات تكون منخفضة للغاية. الواقع أن قابليّة الصيانة هذه أقل بكثير ممّا كان سيكون عليه الحال لو كان “إبداعنا” مقيّدًا. ترجمة - وبتصرّف - للمقال Flexibility Equates to Lower Quality لصاحبه Yegor Bugayenko.
  9. قبل عشرين عامًا، كان المبرمج الجيّد هو ذلك الذي يكون قادرًا على أن يحشر تطبيقًا كاملًا في ملف بامتداد .COM وبحجم 64 كيلوبايت، أما من كان قادرًا على الإتيان بأفضل أداء مع معالج Intel 80386 الضعيف فكان يعدّ واحدًا من أساطين عالم البرمجة. سبب ذلك أنّ الحواسيب كانت في تلك الفترة غالية ومكلفة في حين أن أجور المبرمجين كانت زهيدة، وكانت تلك فترة “عقلية المخترق Hacker mentality”، وقد ولّى ذلك الزمن من غير رجعة، ولم تعد هذه العقلية تلقى أي قبول أو احترام في وقتنا الحاضر، ذلك لأنّ حالة السوق اليوم على النقيض تمامًا. فاليوم أصبحت الحواسيب رخيصة الثمن وأجور المبرمجين مرتفعة، ودخلنا في عصر جديد تسود فيه “عقلية المصمم Designer Mentality” حيث تكون مقروئية الشفرة البرمجية أهمّ بكثير من أدائها. الأسعار مقابل المرتّبات انظر إلى هذا الرسم البياني. يمثّل هذا الرسم البياني مقارنة بين نوعين من التوجهات خلال العقدين الماضيين (1994-2014). ويمكنك أن تلاحظ أن التوجّه الأوّل في تضاؤل تدريجي حيث تصبح ذواكر الحواسيب وأقراص التخزين الصلبة أرخص ثمنًا مع تقدم الزمن. أما التوجّه الثاني فيبين كيف أنّ مرتّبات مطوري البرمجيات في تصاعد مستمر خلال الفترة ذاتها - تضاعفت ثلاث مرّات إن توخّينا الدقة -. ومع أنّي لم أعثر على تقرير رسمي حول هذا الموضوع، إلا أنّي متأكّد بأنّه ليس خافيًا على أحد أنّ مرتّبات المبرمجين في تصاعد مستمر، إذ لم تعد وظيفة مطوّر رئيسي براتب 200,000 دولار في السنة وظيفة الأحلام، في حين أنّ أفضل العروض كانت تصل إلى 60,000 دولار قبل عشرين عامًا. هذا يعني أنّ نفقات إنشاء موقع إلكتروني بلغة PHP سنة 1994 أكثر بـ 1000 مرة من نفقات شراء الأجهزة سنة 2014، وأقلّ بثلاث مرات من نفقات المبرمجين، مع الأخذ بالاعتبار أنّنا نستخدم التكنولوجيا ذاتها، إذ لا نزال نستخدم نظام Linux مع خادوم Apache. الفرق هنا أنّه في سنة 1994 إن كان تطبيقك يواجه مشاكل في الأداء بسبب محدودية الأجهزة، فإنّك ستدفع 35,000 دولار مقابل كل غيغابايت من الذاكرة العشوائية، أما في 2014 فستدفع 10$ فقط. في سنة 1994 كان توظيف المزيد من المبرمجين بهدف تحسين الشفرة البرمجية أو حتى إعادة كتابتها أفضل وأجدى من شراء أجهزة ومعدّات جديدة، أمّا في سنة 2014 فقد انعكست الأمور تمامًا، فقد أصبحت مضاعفة حجم الخادوم أرخص بمرتين (خصوصًا إن كان الخادوم عبارة عن سحابة افتراضية) من أجور تحسين الشفرة البرمجية. في سنة 1994 كان أفضل المبرمجين والمهندسين هم أولئك الذين يمتلكون “عقلية المخترق” أما في 2014 فالتقدير أصبح من نصيب كل من يملك “عقلية المصمم”. عقلية المخترق لو سألت صاحب عقلية المخترق عن رأيه في دالة توليد متسلسلة فابيوناتشي هذه والمكتوبة بلغة Java لأجابك بأنّها “شفرة أنيقة” (هل تظنّ ذلك؟): public int f(int n) { return n>2?f(n-1)+f(n-2):n; } عادة ما يتّصف المخترق الجيّد بالصفات التالية: يستخدم جميع الخصائص المعروفة - وغير المعروفة - في لغة البرمجة التي يستخدمها. الناس عنده صنفان: مخترق ومبتدئ، وهو يكتب الشفرات للمخترقين فقط. يشعر بالملل والإحباط من اتباع القواعد والمعايير. لا يكتب اختبارات الوحدة Unit tests فالمبرمجون الثانويون سيقومون بهذا الأمر لاحقًا. يستمتع بأداء المهام الصعبة، فهكذا تظهر موهبته للعلن. يفضّل استقاء وظائف التطبيق من النقاشات الجانبية على تنفيذ ما يرد في مستند المواصفات Specifications، فذلك أكثر متعة. لا يتحمّل رؤية شخص يجري التعديلات على الشفرة التي كتبها. يفضّل أن يكرّس جهده في مشروع واحد فقط. المخترق شخص موهوب، ويرغب في التعبير عن هذه الموهبة في البرامج التي يكتبها، ويستمتع بهذا الأمر ويقوم به غالبًا بدافع المتعة لا أكثر. يمكن القول إنّه مرتبط بالشفرة التي يكتبها ولا يمكنه تخيّل حياة سعيدة بعد الانفصال عنها، ويشعر المخترق بأنّ الشفرة ملكه وحده ولا يستطيع التفكير بالأمر من منظور آخر. وعندما أتوجه بالسؤال التالي إلى أحد أصدقائي المخترقين: “من سيفهم العمل الذي ستؤدّيه هذه الشفرة؟” يكون الجواب الذي أسمعه بصورة شبه دائمة: “سيسألونني!” - عادة ما ترافق الإجابة نبرة استعلاء مصحوبة بابتسامة صادقة. عقلية المصمّم أما صاحب عقلية المصمّم فسيعيد ترتيب الشفرة السابقة لجعلها سهلة القراءة، وستكون “الشفرة الأنيقة” في نظره بالصورة التالية (ما رأيك أنت؟): public int fibo(final int pos) { final int num; if (pos > 2) { num = fibo(pos - 1) + fibo(pos - 2); } else { num = pos; } return num; } أعتقد أن المصمّم الجيّد يمتاز بالصفات التالية: يميل إلى استخدام تقنيات برمجية تقليدية. يفترض أن جميع الأشخاص مبتدئون ويكتب شفراته على هذا الأساس. يستمتع بوضع القواعد واتباعها. يفضّل الاعتماد على مستند المواصفات بدلا من الأحاديث الجانبية، كما يفضّل أتمتة المهامّ ما أمكن ذلك. تأخذ اختبارات الوحدة معظم وقته. لا يطيق المهام الصعبة والعمل الإضافي. يحب كثيرًا أن يعدّل الآخرون شفرته البرمجية ويحسّنوها. يعمل على عدد من المشاريع في نفس الوقت. المصمّم شخص يمكن أن يتأقلم بسهولة كبيرة مع فريق العمل، وبوسعه المساهمة بفعّالية في طريقة عمل الفريق والمعايير والقواعد والثقافة التي يتّبعها، ويتّسم المصمّم بانضباط كبير إلى جانب مساهمته الكبيرة في الشفرات المصدرية، ويحرص دائمًا على استمرارية عمل شفرته وأفكاره بعد انتهائه من المشروع الذي بين يديه. يصل المصمّم الجيّد إلى أقصى مراتب السعادة عندما يرى أن شفرته قد دخلت في دورة حياتها الطبيعية: التعديل والتحسين والتنقيح والتوقف في نهاية المطاف. ويشعر المصمّم بأن الشفرة التي يكتبها هي أحد أبنائه، وإن أصبح هذا الابن قادرًا على التحدّث والحركة، فعليه أن يعيش حياته الخاصة. المستقبل إن كنت تعتبر نفسك مخترقًا، فأعتقد أنّه قد حان وقت التغيير، فعصر المخترقين في أفول. قد نتوقّف عن التفكير في المستقبل القريب بمنطق “الأجهزة” وستعمل تطبيقاتنا على منصّات حاسوبية مرنة تتمتع بذاكرة وقوة معالجة وسعة تخزينية غير محدودة. وبكل بساطة، سننفق أموالنا في الاستفادة من الموارد وستكلّف أي مشكلة في الأداء القليل من الدوﻻرات الإضافية في فواتيرنا الشهرية، ولن نعبأ بمسألة تحسين الشفرة على الإطلاق. وفي الوقت نفسه، سيتطلّب التعامل مع مهندس للبرمجيات أموالًا أكثر، وقد يطلب 500$ أو أكثر للساعة الواحدة مقابل تفحّص البرنامج وتشخيص المشكلة، كما هو الحال مع المحامين وأطباء الأسنان. هذا هو السبب الذي يدفع المستثمرين في بناء برنامج جديد إلى وضع جلّ اهتمامهم في مسألة قابلية المنتج للصيانة Maintainability. وسيدرك مموّلو المشاريع أن أفضل منتج يمكن الحصول عليه مقابل أموالهم هو ذلك المنتج الذي يتمتع بأعلى قدر من المقروئية وقابلية الصيانة والأتمتة، وليس أعلى قدر من السرعة. ترجمة - وبتصرّف - للمقال Are You a Hacker or a Designer? لصاحبه Yegor Bugayenko. حقوق الصورة البارزة محفوظة لـ Freepik
  10. قد يكون تقديم الاستشارات وسيلة ممتازة لتمويل شركتك الناشئة أو للحصول على بعض الأرباح، فالأمر سهل للغاية، وكلّ ما عليك فعله هو تحديد الأجور المطلوبة والبدء بالعمل. ولكن بعد فترة وجيزة ستلاحظ أنّ هناك الكثير من ساعات العمل التي يمكن أن تدرّ عليك المزيد من الأموال، ولربما تدفعك هذه الملاحظة إلى توسيع العمل، فتوظّف شخصًا ما مقابل 30 دولارا للساعة، وتعدّل الأجر الذي تطلبه إلى 60$. أرباح سهلة، أليس كذلك؟. في الواقع، لا تجري الأمور بهذه البساطة، وسأبيّن لك فيما يلي ما سيحدث على أرض الواقع، وسأقدّم إليك بعض النصائح لتجنب ذلك. مضاعفة الأجور لا يعني شيئًا لنفترض أنّك وظّفت شخصا براتب 60,000$ في السنة. عدد ساعات العمل في السنة الواحدة هو 2,000 ساعة (50 أسبوعًا × 40 ساعة في الأسبوع)، ويبقى أسبوعان للإجازات، وبهذا يكون أجر الساعة: الأجر الاسمي: 30$ للساعة إن أعدنا الحسبة باعتبار أيام العمل، فهناك 250 يوم عمل في 50 أسبوعًا. العطل الرسمية في الولايات المتحدّة هي 10 أيام، وهذا يعني أنّ عدد أيام العمل يصبح 240 يومًا. والآن إن أعدنا حساب كلفة الساعات نحصل على: 20 يوم عمل في الشهر، 31$ للساعة. تختلف القوانين والضرائب المفروضة على التوظيف في الولايات المتحدة الأمريكية بين ولاية أخرى، ولكن القاعدة العامة هي دفع 15% في الضرائب التي تشمل الرعاية الصحية والضمان الاجتماعي. سيؤدي هذا إلى تغيير الراتب السنوي لموظّفك من 60,000، ولكنّك ستستثقل دفع المال في الأيام التي لا يوجد فيها عمل. هل تقديم الاستشارات بهذا السوء؟ تقديم الاستشارات من الأعمال المدرّة للأرباح وهي كذلك طريقة ذكية لتمويل الشركة الناشئة ذاتيًا. لا يتطلّب تحقيق النجاح في هذا المجال سوى تجاوز هذه العقبات. فعلى سبيل المثال، أنت تعلم جيّدًا أنّ من الضروري أن تحاسب موظّفيك مقابل 40 ساعة كاملة من العمل، لذا عوّد نفسك على متابعة ساعات العمل كل أسبوع لتتأكد من عدم وجود تأخير من قبل أحد الموظفين، كذلك لا تنسَ تقديم بعض الحوافز للموظفين وشاركهم الأرباح عندما يبذلون جهودًا إضافية في العمل. تقديم الاستشارات عمل صعب، ولا تجني معظم شركات تقديم الاستشارات الكثير من الأرباح، ومن النادر جدًّا أن تجد شركة تلتزم بإطلاق منتج ناجح كانت قد طوّرته في فترات انقطاع العمل. لذا إن كنت ترغب في تحقيق النجاح، يجب عليك أن تكون جادًّا وملتزمًا وأن لا تستسلم على الإطلاق، وستتمكن من ذلك بكل تأكيد. وإن حدث ذلك، فستكون قد نجحت في تحقيق التمويل الذاتي لشركتك الناشئة، وحصلت على عمل يضمن لك مصاريف المعيشة، وأبعدت عن شركتك الناشئة الكثير من المشاكل الناجمة عن الاعتماد على المنتج حصرًا، إضافة إلى تأسيس فريق عملٍ متميّز. هل لديك أي خبرة في هذا المجال، شاركنا نصائحك وخبراتك في التعليقات. ترجمة - وبتصرّف - للمقال The unfortunate math behind consulting companies لصاحبه Jason Cohen. حقوق الصورة البارزة محفوظة لـ Freepik
  11. تتمتع جميع أنظمة التشغيل المعروفة بخاصية تعدد المستخدمين، ويقدّم نظام Microsoft Windows 10 مجموعة من الخيارات التي تساعد على تخصيص تجربة استخدام النظام لكل مستخدم، فيمكن مثلًا إنشاء حسابات خاصة لكل فرد من أفراد العائلة ليتمتع كل واحد منهم بتجربته الخاصة في استخدام النظام، إذ يمكن لكل فرد أن يختار إعدادات سطح المكتب الخاصة به كخلفية الشاشة وشاشة التوقف وغير ذلك. ولكن قبل الحديث عن إدارة المستخدمين في نظام ويندوز يجب علينا التعرّف على أنواع الحسابات التي يقدّمها هذا النظام وخصائص كلّ منها. أنواع الحسابات في نظام ويندوز يقدّم نظام ويندوز نوعين أساسين من الحسابات هما: حساب المستخدم العادي Standard User Account، حساب المستخدم المدير Adminstrator User Account. 1. حساب المستخدم العادي هذا الحساب هو الحساب المبدئي في نظام ويندوز، ويمكّن هذا الحساب المستخدمين من أداء المهام اليومية وذلك من خلال السماح بتشغيل البرامج وإنشاء الملفات والمستندات وتعديل بعض الإعدادات البسيطة في النظام، وعادة ما تكون هذه التعديلات مقتصرة على حساب المستخدم، ولا تأثير لها على الحسابات الأخرى، إضافة إلى عدم إمكانية إجراء تعديلات على مستوى النظام كتثبيت برامج جديدة مثلًا. في ما يلي بعض الصلاحيات الممنوحة لحساب المستخدم العادي: تغيير الإعدادات الشخصية للمستخدم، مثل خلفية سطح المكتب، كلمة المرور، أصوات النظام، وشاشة التوقف. التعامل مع الوسائط المحمولة مثل ذاكرة الفلاش وأقراص CD و DVD والبلوتوث. الاتصال بالشبكات السلكية واللاسلكية. استخدام سطح المكتب البعيد Remote Desktop (في حال توفّره) للاتصال بحواسيب بعيدة. 2. حساب المستخدم المدير يقدّم هذا النوع من الحسابات - كما هو واضح من اسمه - صلاحيات أوسع بكثير من النوع السابق، إذ يمكن لصاحب هذا الحساب أن يجري تعديلات كبيرة على مستوى النظام وأن يثبت برامج جديدة، إضافة إلى القدرة على إنشاء وتعديل حسابات المستخدمين الآخرين وتحديد الصلاحيات الممنوحة لكل مستخدم. إليك جزءًا من الصلاحيات الممنوحة لهذا الحساب: تثبيت برامج جديدة على جهاز الحاسوب. إضافة أجهزة جديدة وتثبيت تعريفاتها Drivers في الجهاز. ضبط عملية التحديث التلقائي للنظام. الوصول إلى ملفات النظام وإجراء التعديلات عليها. ضبط وتهيئة الجدار الناري Firewall لنظام ويندوز. إجراء عملية النسخ الاحتياطي للنظام واستعادة النسخ الاحتياطية السابقة. إنشاء وتعديل وحذف حسابات المستخدمين والتحكم في الصلاحيات الممنوحة لكل حساب. إنشاء الحسابات في نظام ويندوز بعد أن تعرفنا على أنواع الحسابات التي يوفّرها نظام ويندوز، سنشرع بالحديث عن كيفية إنشاء هذه الحسابات وكيفية إدارتها. الحساب الأول عند تثبيت نظام ويندوز سيطلب منك النظام إدخال اسم وكلمة مرور. سيستخدم النظام هذه المعلومات لإنشاء أول حساب لك في نظام ويندوز من نوع حسابات المدير. إن كنت أنت الشخص الوحيد الذي يستخدم جهاز الحاسوب، فعلى الأرجح أنّك لن تجد حاجة إلى إنشاء حسابات أخرى، ولكن يمكنك من خلال هذا الحساب إنشاء حسابات جديدة، وبعد إنشائها يمكنك إجراء التعديلات ومنح الصلاحيات التي تريدها لكل حساب، كذلك يمكنك حذف الحسابات الموجودة حسب الحاجة. إنشاء حساب جديد في ويندوز لتنشئ حسابًا جديدًا في ويندوز توجّه إلى الإعدادات Settings من قائمة ابدأ Start. انقر على أيقونة الحسابات Accounts. انقر على العائلة وأشخاص آخرون Family and other users في القائمة الجانبية. انقر على أيقونة إضافة شخص آخر إلى الحاسوب Add someone else to this PC. في النافذة المنبثقة انقر على ليست لديّ معلومات تسجيل الدخول الخاصّة بهذا الشخص I’dont have this person’s sign-in information. انقر على إضافة مستخدم بدون حساب مايكروسوفت، Add a user without a Microsoft account. ادخل المعلومات المطلوبة (الاسم، كلمة المرور، وعبارة تساعد تذكر كلمة المرور)، ثم اضغط على زرّ التالي Next، وبهذا يُنشَأ حساب جديد في نظام ويندوز 10. يطلق نظام ويندوز 10 على الحساب الجديد اسم حساب محلّي Local account والمقصود أنّ هذا الحساب غير مرتبط بحساب Microsoft (سنتحدث عن حسابات Microsoft بعد قليل). يفتقر الحساب المحلي إلى بعض الميزات، إذ ليس بالإمكان مزامنة الملفات عبر الأجهزة التي تعمل بنظام ويندوز 10، كذلك لا يمكن إضافة الحسابات العائلية إلى النظام (سنتطرق إلى هذه التفاصيل في مقال منفصل)، ولن تستفيد من كورتانا Cortana المساعد الرقمي الخاصّ بويندوز، ولن تكون قادرًا على تحديد موقع جهازك في حال فقدانه. تحويل الحساب الجديد إلى حساب المدير تكون جميع الحسابات الجديدة التي تنشئها في ويندوز 10 من النوع العاديّ، وهذا يعني أنّ المستخدم الجديد سيكون محدود الصلاحيات، ولن يكون قادرًا على القيام بالكثير من الأمور إلا بإدخال كلمة المرور الخاصّة بالمستخدم المدير. ولكي تمنح صلاحيات المدير إلى المستخدم الجديد يجب عليك تحويل حسابه إلى حساب مدير، وذلك باتباع الخطوات التالية. توجّه إلى الإعدادات Settings من قائمة ابدأ Start ثم توجّه إلى الحسابات Accounts. بعد ذلك انقر على أيقونة العائلة وأشخاص آخرون Family and other users في القائمة الجانبية. عند النقر على الحساب الذي ترغب في تحويله إلى مدير تظهر أيقونتان اختر منهما تغيير نوع الحساب Change account type. اختر المدير Administrator من القائمة المنسدلة واضغط على موافق OK. حذف الحسابات يمكنك حذف الحسابات التي أنشأتها مسبقًا من خلال النقر على الحساب المراد حذفه ثم النقر على زرّ حذف Remove. تغيير الصورة الشخصية يتيح نظام ويندوز تغيير الصورة الشخصية المرتبطة بالحساب، وذلك إما بالتقاط صورة بواسطة كاميرا الجهاز أو اختيار صورة مخزّنة في الحاسوب. ولتغيير الصورة الشخصية توجّه إلى الإعدادات ثم الحسابات وانقر على زرّ معلوماتك Your info. انقر على زرّ الكاميرا Camera لتشغيل كاميرا الجهاز والتقاط الصورة الشخصية، أو انقر على ابحث عن صورة Browse for one لاختيار صورة مخزّنة في الجهاز. تغيير كلمة المرور الخاصة بحساب معين لتغيير كلمة المرور الخاصة بحساب معين يجب تسجيل الدخول إلى ذلك الحساب. توجه إلى الإعدادات Settings ثم انقر على زرّ الحسابات Accounts، وانقر على زرّ خيارات تسجيل الدخول Sign-in options. يقدّم نظام ويندوز 10 أساليب مختلفة لتأمين حسابك، فإلى جانب كلمة المرور التقليدية، يمكن تأمين الحساب بواسطة خدمة Windows Hello حيث يمكنك تسجيل الدخول بعد أن يتعرّف النظام على وجهك من خلال كاميرا الجهاز. كذلك يمكن استخدام رمز PIN للولوج إلى النظام، أو استخدام كلمة المرور الصورية Picture password، حيث تخصّص صورة تظهر عند رغبتك في الولوج إلى النظام، ومن خلال رسم أشكال وخطوط محدّدة على تلك الصورة، يكون بمقدورك تسجيل الدخول، وهذه الطريقة مفيدة في الأجهزة التي تدعم خاصية اللمس. لتغيير كلمة المرور انقر على زرّ تغيير Change في قسم كلمة المرور Password. اكتب كلمة المرور الحالية في النافذة المنبثقة. ثم اكتب كلمة المرور الجديدة. حسابات Microsoft إن كنت تستخدم إحدى خدمات Microsoft، مثل Outlook.com، Hotmail، Skype، OneDrive وغيرها فأنت تمتلك بالفعل حساب Microsoft. ويمكن الاستفادة من هذا الحساب في الوصول إلى تطبيقاتك في متجر ويندوز Windows Store ومزامنة إعداداتك على جميع أجهزتك التي تعمل بنظام ويندوز 10. إن كنت لا تملك حساب Microsoft فيمكنك التوجّه إلى العنوان www.hotmail.com وإنشاء حساب جديد. بعد ذلك توجّه إلى الإعدادات ثم الحسابات ثم انقر على زرّ معلوماتك Your info في القائمة الجانبية. بعد ذلك انقر على تسجيل الدخول بواسطة حساب مايكروسوفت Sign in with a Microsoft account insted، لتظهر النافذة التالية. أدخل المعلومات المطلوبة في هذه النافذة ثم اضغط تسجيل الدخول Sign in. قد يطلب منك النظام إدخال كلمة المرور التي تستخدمها حاليًا؛ لذا أدخلها واضغط على زرّ التالي Next. بعد تسجيل الدخول بواسطة حساب Microsoft، يمكنك اختيار الإعدادات التي ترغب في مزامنتها عبر الأجهزة التي تستخدمها والتي تعمل بنظام ويندوز 10، وذلك بالتوجّه إلى الإعدادات ثم الحسابات ثم النقر على زرّ زامن إعداداتك Sync your settings. يمكنك الآن تحديد الإعدادات التي ترغب في مزامنتها مثل سمة النظام Theme، وإعدادات المتصفح وكلمات المرور واللغة وغيرها.
  12. بناء المواقع الإلكترونية أمر ممتع ومحبط في نفس الوقت، وعادة ما يكون سبب الإحباط هو السعي لتوحيد مظهر وأداء الموقع الإلكتروني في جميع المتصفحات، ولا شكّ أن مصمّمي الواجهات الأمامية قد مرّوا بحالة الإحباط هذه بشكل أو بآخر. ولكن الحقيقة تقول بأنه ما من ضرورة تدعو إلى توحيد مظهر وأداء الموقع الإلكتروني على جميع المتصفحات، فطريقة عمل الموقع في كل متصفّح عائدة إلى المبرمج وإلى طبيعة الموقع الإلكتروني. فإن كان الموقع يستقبل أقلّ من نصف نسبة الزوّار من خلال Internet Explorer 8 فقد يكون من المعقول حينئذٍ صرف النظر عن هذا المتصفح، أما لو كان نصف الزوّار يستخدمون هذا المتصفح وكانت هذه الزيارات تدرّ أموالًا كثيرة، فسيكون دعم هذا المتصفح حينئذٍ أمرًا إلزاميًا. من هنا يجب تحديد ما هو مقبول بالنسبة لموقع إلكتروني معين والعمل بموجب ذلك. يوجد عدد من الممارسات الجيدة التي تتيح للموقع الإلكتروني أن يعمل على نحو جيّد في جميع المتصفحات. ينصح عادة بتوفير الدعم التراجعي Fallback support عند استخدام خصائص CSS 3 وذلك لدعم المتصفحات القديمة، وهناك تقنيات أخرى مثل الـ Shivs و Polyfills وهي عبارة عن ملحقات JavasScript صغيرة تضيف الدعم لعدد من المزايا المطلوبة التي لا تدعمها المتصفحات القديمة مبدئيًّا. HTML Shiv أحد أشهر الملحقات التي تقدّم الدعم التراجعي هو HTML5 Shivs، وقد أنشأ Remy Sharp هذا الملحق لإتاحة استخدام عناصر HTML 5 في Internet Explorer 8 وما قبله، ولا يكتفي هذا الملحق بتوفير الدعم لعناصر HTML5 فقط، بل يتجاوز ذلك إلى إمكانية تنسيقها بواسطة CSS. يجب تنزيل آخر إصدار من هذا الملحق من Google حيث يحتفظ Remy بآخر الإصدارات، ثم ضيافتها على الخادوم الخاص بك. وللحصول على أفضل أداء يفضّل الإشارة إلى ملف JavaScript الخاص بالإضافة في بداية الصفحة ضمن الوسم <head>، مباشرة بعد الإشارة إلى ملفات CSS. كذلك يجب وضع شفرة جلب الإضافة ضمن تعليق مشروط لتضمن تنزيل الملفات في الإصدار الثامن من Internet Explorer وما دونه. <!--[if lt IE 9]> <script src="html5shiv.js"></script> <![endif]--> وبعد إنشاء عناصر HTML5 الجديدة في مستوى Block باستخدام HTML5 Shiv يجب تحديد وتحديث تلك العناصر بواسطة التصريح display:block. article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary { display: block; } لا يعرّف الإصداران الثامن والتاسع من Internet Explorer أنماط بعض عناصر HTML5 بطريقة صحيحة في مستوى Inline-block، لذا تجب إضافة التصريح display: inline-block إلى هذه العناصر. بهذا يمكنك استخدام أي عنصر من عناصر HTML5 في جميع إصدارات Internet Explorer. audio, canvas, video { display: inline-block; } ما الفرق بين Shiv و Shim قد تسمع أو تقرأ في بعض المواقع عن HTML5 Shim، وقد تتساءل إن كان هناك فرق بينها وبين HTML5 Shiv. في الواقع لا يوجد أي فارق يذكر بين هذين المصطلحين، وكلاهما يستخدمان للتعبير عن المعنى ذاته. الكشف عن المزايا المتاحة في المتصفح تؤدي HTML5 Shiv عملًا جيّدًا مع التعليقات المشروطة في تعريف المتصفح بخصائص HTML5 وعناصرها الجديدة، ولكن باستخدام هذه الطريقة فإنك تحدّد مسبقًا المتصفح الذي لا يدعم هذه الخصائص. هناك طريقة أخرى لتوفير الدعم لعناصر وخصائص معينة في HTML5 و CSS3 بغض النظر عن المتصفح المستخدم. توفّر مكتبة Modernizr وسيلة للكشف عن ميزات المتصفح من خلال كتابة شفرات CSS و JavaScript مشروطة بالاستناد إلى دعم المتصفح لخاصّية معينة أم لا. فعلى سبيل المثال، إن كان أحد المتصفحات يدعم خاصّية الأركان الدائرية فستقوم Moderinzr بإضافة الصنف borderradius إلى عنصر html. أما إن كان المتصفح لا يدعم هذه الخاصّية، تقوم مكتبة Modernizr بإضافة الصنف no-borderradius إلى عنصر html. تنزيل مكتبة Modernizr لتشغيل مكتبة Modernizr في موقعك توجّه إلى صفحة التنزيل الخاصة بالمكتبة حيث يمكنك اختيار الخصائص التي ترغب في الكشف عنها. بعد تنزيل المكتبة ارفعها إلى الخادوم الخاص بك ثم أشر إلى الملف ضمن الوسم head في صفحة HTML مباشرة بعد آخر إشارة لملفات الأنماط. من الجدير بالذكر أنّه يمكن تخصيص Modernizr لتتضمن HTML5 Shiv، وبهذا لا حاجة للإشارة إليها في بداية Modernizr. <script src="modernizr.js"></script> التطبيق الشرطي لأنماط CSS بمجرد أن تبدأ مكتبة Modernizr بالعمل يصبح بالإمكان تطبيق أنماط CSS بالاعتماد على وجود أو عدم وجود خصائص معينة في المتصفح الذي يعرض الموقع الإلكتروني. تستطيع Modernizr الكشف عن معظم الخصائص والقيم التي تقدّمها CSS3 ويمكنك التعرّف عليها من خلال توثيقات المكتبة على شبكة الإنترنت. يجدر الانتباه إلى أنّه قد يكون من غير الضروري الكشف عن الخصائص المتعلقة ببعض الأنماط، فعلى سبيل المثال عندم استخدام قيمة لون بصيغة RGBa يمكن تقديم دعم تراجعي لهذه القيمة من خلال توفير قيمة اللون بصيغة hexadecimal دون الحاجة إلى اللجوء لتقنية الكشف عن الخصائص. عندما تقرّر استخدام تقنية الكشف عن الخصائص من الضروري أن تحافظ على تنظيم الشفرات وأن تراعي مسألة الأداء. تجنّب قدر الإمكان تكرار الشفرة لأكثر من مرة أو إنشاء طلبات HTTP إضافية. button { border: 0; color: #fff; cursor: pointer; font-size: 14px; font-weight: 600; margin: 0; outline: 0; } /* With CSS Gradient Styles */ .cssgradients button { border: 1px solid #0080c2; background: linear-gradient(#00a2f5, #0087cc); border-radius: 6px; padding: 15px 30px; } .cssgradients button:hover { background: linear-gradient(#1ab1ff, #009beb); } .cssgradients button:active { box-shadow: inset 0 1px 10px rgba(255, 255, 255, .5); } /* Without CSS Gradient Styles */ .no-cssgradients button { background: transparent url("button.png") 0 0 no-repeat; padding: 16px 31px; } .no-cssgradients button:hover { background-position: 0 -49px; } .no-cssgradients button:active { background-position: 0 -98px; } في المثال السابق تمكن ملاحظة أن الزرّ يرث أنماطًا مبدئية، ولكن الأنماط المخصّصة تطبّق بالاستناد إلى دعم المتصفح لخاصية الخلفية المتدرّجة في CSS3، إضافة إلى خاصيتيْ الأركان الدائرية والظلال. إن كان المتصفح يدعم هذه الخصائص، فسيحصل الزرّ على خلفية متدرجة وعلى ظلّ وحواف دائرية، أما في حال عدم دعم المتصفح لهذه الخصائص فيحصل الزرّ على صورة خلفية تتضمن جميع هذه التأثيرات. بهذه الطريقة لا توجد شفرات زائدة ولا يُرسَل طلب HTTP إلا عند الحاجة. عند العمل مع تقنية الكشف عن خصائص CSS3 يكون من الصعب تخمين مظهر عنصر معين في المتصفحات التي لا تدعم الخصائص الجديدة، ولحسن الحظ هناك أداة تدعى deCSS3 تعمل على تعطيل جميع خصائص CSS3 في الصفحة ما يسمح لك بالتعرف على مظهر موقعك الإلكتروني عندما لا تُستخدَم CSS3، إضافة إلى اختبار الأنماط المشروطة المستخدمة في الموقع. لتحصل على فكرة سريعة عن الخصائص والميزات التي يدعمها متصفّح معين،زر الموقع haz.io باستخدام ذلك المتصفح. التنزيل الشرطي للملفات إضافة إلى التنزيل الشرطي للأنماط، تقدّم مكتبة Modernizr وسيلة لاستخدام تقنية الكشف عن الخصائص في JavaScript، حيث يصبح بالإمكان تنزيل ملفات الـ Polyfills وغيرها من الملفات بالاستناد إلى وجود خاصية معينة والاستعانة بمكتبة jQuery وتابع getScript الخاص بهذه المكتبة. يمكن استخدام مكتبة Modernizr في العبارات الشرطية وذلك لتشغيل شفرات متنوعة اعتمادًا على تحقق الشرط أو عدمه. في المثال التالي تتحقّق Modernizr من دعم المتصفح لخاصية التخزين المحلي Local storage. إن كانت هذه الخاصّية متوفرة في المتصفح، يُنزَّل الملف storage.js باستخدام jQuery وتابع getScript، أما إن كانت هذه الخاصّية غير مدعومة في المتصفح فيُستعان بـ jQuery لتنزيل ملف storage-polyfills.js وبواسطة التابع ذاته. $(document).ready(function() { if (Modernizr.localstorage) { // التخزين المحلّي متوفّر jQuery.getScript('storage.js'); } else { // التخزين المحلّي غير متوفّر jQuery.getScript('storage-polyfill.js'); } }); التنزيل الشرطي بالاستناد إلى استعلام الوسيط Media Query استعلامات الوسائط Media queries من الأمور المهمّة التي يمكن لـ Modernizer الكشف عن وجودها ، حيث يمكن تنزيل الملفات فقط في حال تحقق الشروط الخاصة باستعلام الوسائط. تجنّب تنزيل الملفات غير الضرورية من الأمور المفيدة جدًّا في تحسين أداء الصفحة. $(document).ready(function() { if (Modernizr.mq('screen and (min-width: 640px)')) { jQuery.getScript('tabs.js'); } }); تتحقق Modernizer في المثال الموضح أعلاه ممّا إذا كان عرض الشاشة أكبر من 640 بكسلًا، أي شاشات حواسيب سطح المكتب بصورة رئيسية، ثم تُنزِّل ملف tabs.js بالاعتماد على تحقق الشرط. من الضروري الانتباه إلى أن التحقق من هذا الشرط يتم مرة واحدة فقط وذلك عند تنزيل الصفحة، وهذا يعني أنّه لن يُتحقَّق من هذا الشرط عندما يغيّر المستخدم حجم الصفحة، وإن تطلب الأمر إعادة التحقق من جديد، فيجب استخدام شفرة JavaScript إضافية للقيام بذلك. التشغيل الشرطي للشفرات يمكن التحقق من جميع الخصائص التي تقدّمها HTML5 و CSS3 باستخدام Modernizr ومن خلال JavaScript. فعلى سبيل المثال، يستحسن تعطيل عمل التلميحات Tooltips في أجهزة الهاتف المحمول نظرًا لعدم توفّر التحليق Hover في هذه الأجهزة، والاستعاضة عن ذلك بعرض التلميحات على هيئة نصوص صرفة. يمكن تغليف الشفرة المسؤولة عن استدعاء التلميحات بعبارة شرطية تمنع تنزيل الشفرة عند استعراض الموقع في الشاشات الصغيرة. $(document).ready(function() { if (Modernizr.mq('screen and (max-width: 400px)')) { $('.size').text('small'); } }); يوضح المثال السابق على نحو مبسط طريقة تنفيذ شفرة JavaScript بالاستناد إلى شرط وضعته Modernizr. إن كان عرض الشاشة عند تنزيل الصفحة أكبر من 800 بكسل فلن يحدث شيء. ولكن إن كان عرض الشاشة أصغر من هذا الرقم تُبْدَل ‘large’ بكلمة ‘small’. HTML5 و CSS3 Polyfills تتوفّر في الوقت الحاضر polyfills لمختلف مزايا HTML5 وCSS3 تقريبًا، وقد وفّر الفريق المسؤول عن تطوير Modernizr قائمة مفصّلة بالـ polyfills المتوفّرة، والتي يمكن استخدامها حسب الحاجة. كذلك وضع نفس الفريق قائمة بجميع ميزات HTML5 و CSS3 الجديدة إضافة إلى تعليمات استخدام هذه الميزات بصورة صحيحة. يجب الانتباه إلى أنّه لا تحتاج جميع هذه الميزات إلى الـ Polyfills، إذ يمكن الاستفادة من بعضها مباشرة أو من خلال الدعم التراجعي. اختبار الموقع في متصفحات مختلفة ربّما تكون عملية اختبار الموقع الإلكتروني سواء من ناحية التصميم أو التطوير في المتصفحات أمرًا متعبًا للغاية، وبصورة عامة فإنّ المتصفحات الحديثة مثل Chrome، Firefox و Safari تعمل جيّدًا، إلا أن غالبية المشاكل تظهر في متصفح Internet Explorer، إضافة إلى أن اختبار إصدارات مختلفة من هذا المتصفح قد يكون أمرًا صعبًا. هناك العديد من الخدمات التي تساعد على اختبار الموقع الإلكتروني في المتصفحات المختلفة، بعضها تفاعلي وبعضها لا. تساعد القدرة على التفاعل مع المتصفح بدلًا من رؤية لقطة شاشة للموقع كثيرًا على تنقيح الشفرة البرمجية. ومن أفضل الطرق لاستخدام إصدارات مختلفة من متصفح Internet Explorer هي استخدام الآلات الافتراضية Virtual Machines، بحيث تتضمن كل آلة إصدارًا مختلفًا من هذا المتصفح. يظهر في الصورة أعلاه برنامج VirtualBox يعمل على نظام Mac OS X مع المتصفح Internet Explorer من الإصدار السادس إلى التاسع. تقدّم Microsoft مجموعة من الحواسيب الافتراضية Virtual PCs التي يمكن استخدامها في عملية الاختبار، ولكن يمكن لعملية الإعداد أن تكون مرهقة وصعبة. لحسن الحظ قام Greg Thornton ببناء أداة تُؤَتْمِت عملية تنصيب الآلات الافتراضية. تستغرق عملية التنصيب بعض الوقت وذلك لتنزيل جميع الآلات الافتراضية المتوفرة، وستحتاج كذلك إلى مساحة كبيرة على القرص الصلب، ولكن يمكن تثبيت الآلات الافتراضية التي تحتاج إليها فقط، وبالنظر إلى وتيرة استخدامك لهذه الآلات الافتراضية، قد يكون من الأفضل تنصيبها على قرص صلب خارجي. يحتوي الإصدار الثامن من Internet Explorer على أدوات تطوير مضمّنة في المتصفح، ولكن الإصدارات السابقة تفتقر إلى هذه الميزة. ولكن تتوفّر أداة Firebug التي تقدّم أدوات تطوير مفيدة جدًّا داخل المتصفّح. يظهر في الصورة أعلاه الإصدار السابع من Internet Explorer يعمل في آلة افتراضية مع استخدام أداة Firebug Lite لتنقيح الشفرة البرمجية. ترجمة - وبتصرّف - للمقال Feature Support & Polyfills لصاحبه Shay Howe.
  13. يشبه أداء المواقع كثيرًا قاعدة 80/20، حيث إن إجراء تحسينات بنسبة 20% ستزيد سرعة الموقع الإلكتروني بنسبة 80%. فاتّخاذ خطوات بسيطة مثل تنظيم شفرة CSS يأتي بثمار طيّبة. يشرح هذا المقال خطوات بسيطة يمكن أن يؤدّي تطبيقها - إلى جانب تنظيم شفرة CSS - إلى تحسّن ملحوظ في أداء الموقع. تصغير الملفات وضغطها تعدّ إزالة الشفرة المتكررة وغير الضرروية من أبسط الطرق وأفضلها في تقليل حجم ملف CSS، ولكن هناك بعض الطرق الأخرى. تتمثّل إحدى هذه الطرق في تصغير minify وضغط ملفات HTML و CSS و JavaScript. كذلك يمكن ضغط الصور وإزالة التعليقات و مواصفات الألوان Color Profiles غير الضرورية. آلية gzip للضغط آلية gzip هي إحدى أشيع وسائل ضغط الملفات، إذ تأخذ هذه الآلية الملفات الشائعة مثل HTML ،CSS، Javascript وما شابهها وتبدأ بتحديد السلاسل النصية المتشابهة وضغطها. وكلما تم التعرّف على المزيد من السلاسل النصية كان حجم الملفات المضغوطة أصغر، وهذا يعني إرسال ملفات أصغر حجمًا من الخادوم إلى المتصفح. ليست تهيئة gzip بالأمر الصعب، وقد أبلى فريق HTML5 Boilerplate بلاءً حسنًا في هذا المجال. ولضغط الملفات بآلية gzip ستحتاج إلى إضافة ملف .htaccess إلى المجلد الرئيسي في خادوم الويب ثم تدرج في هذا الملف أنواع الملفات التي ترغب في ضغطها. لا تنسَ إضافة النقطة إلى بداية اسم الملف، لأنّ هذا الملف هو من الملفات المخفية. يمكنك أن تجد في إعدادات خادوم Apache في HTML5 Boilerplate أنواع الملفات التي يُنصح بضغطها، ولا بأس بالتذكير مرة أخرى أنّ شفرة ضغط هذه الملفات يجب أن تكون في ملف .htaccess في المجلد الرئيسي لخادوم الويب. وجدير بالذكر أن هذا الملف لا يعمل إلا مع خواديم Apache والتي تتطلب تفعيل الوحدات التالية. mod_setenvif.c mod_headers.c mod_deflate.c mod_filter.c mod_expires.c mod_rewrite.c لا تقلق إن وجدت صعوبة في هذا الأمر، فهناك بعض الخواديم التي تهيّئ لك هذه الأمور نيابة عنك، فبطبيعة الحال، ضغط الملفات وتصغير حجمها يكون دائمًا في مصلحة خادوم الويب. قياس نسبة الضغط تقدّم أداة الفحص Inspector في متصفح Google Chrome عددًا كبيرًا من المعلومات حول أداء الصفحة، ويمكن الوصول إليها من خلال التبويب Network. إضافة إلى ذلك، هناك عدد من المواقع الإلكترونية التي تساعد على معرفة ما إذا كانت خاصية الضغط gzip مفعّلة أم لا. في تبويب Network يتم التعرّف على جميع الملفات المحمّلة بواسطة المتصفح وعرض أحجام تلك الملفات والمدة التي استغرقتها عملية التحميل. لاحظ كيف أن ضغط الملفات قد قلّل من حجمها بنسبة 60% تقريبًا. يمكن التعرّف على أنواع وسائل الضغط التي يدعمها المتصفح من خلال اختيار أحد الملفات. في الصورة أعلاه نلاحظ أنّ المتصفح يدعم gzip، deflate و sdch كما هو مبيّن في ترويسة الطلب المرسل من المتصفح. أما ترويسة Header الإجابة الواردة من الخادوم فتبيّن أن الملف مضغوط بواسطة gzip. ضغط الصور قد يكون تقليل حجم الملفات النصّية مفيدًا، ولكن يمكن الحصول على نتائج أفضل من خلال ضغط ملفات الصور. يمكن لحجم ملفات الصور المستخدمة في الموقع الإلكتروني أن يصبح كبيرًا جدًّا، وضغط الصور يمكن أن يساعد على إبقاء حجم الملفات الكلي تحت السيطرة. يتجنّب الكثيرون مسألة ضغط الصور وذلك خوفًا من أن يتسبب الضغط في تقليل جودة الصورة، ولكن هذا غير صحيح في أغلب الأحيان، إذ يمكن ضغط الصورة دون المساس بجودتها، وذلك من خلال التخلص من مواصفات الألوان والتعليقات غير الضرورية. تتوفّر العديد من الأدوات التي تساعد على ضغط الصور، من أفضلها ImageOptim لنظام Mac و PNGGauntlet لنظام Window، و Trimage لنظام لينكس. تقوم هذه الخدمات بضغط صيغ الصور الأكثر شيوعًا مثل JPG و PNG. نموذج لضغط الصور غير مضغوطة، 455kb مضغوطة، 401kb باستخدام ImageOptim فإنّ حجم الصورة أعلاه قد انخفض بمقدار 14% دون المساس بدقّة الصورة أو جودتها. من الجدير بالذكر هنا أن اختيار أبعاد الصورة في HTML باستخدام خاصيتي height و width يساعد على تصيير الصورة بسرعة أكبر وذلك بتحديد الحجم المناسب للصورة. ولكن يجب الانتباه إلى أن هاتين الخاصيتين تستخدمان في تحديد أبعاد الصورة الحقيقية لا لتصغير حجمها، فمن الممارسات السيئة في هذا الصدد استخدام صورة ذات حجم كبير ثم تصغيرها باستخدام خاصية height و width، والنتيجة تحميل بيانات فائضة عن الحاجة. <img src="ocean.jpg" height="440" width="660" alt="Oceanview"> تقليل طلبات HTTP يشكّل عدد طلبات HTTP - بعد حجم الملفات - العائق الأكبر في طريق تحقيق الأداء الأفضل. ففي كل مرة يُرسل فيها الطلب إلى الخادوم يزداد وقت تحميل الصفحة، وفي بعض الأحيان تتطلب معالجة الطلب من قبل الخادوم الانتهاء من معالجة الطلب السابق، وقد تؤدّي كثرة الطلبات إلى حدوث مشاكل في الخادوم. دمج الملفات المتشابهة إحدى الطرق المتّبعة في تقليل طلبات HTTP - وربّما أسهلها - تتمثّل في دمج الملفات المتشابهة، بمعنى دمج جميع ملفات CSS في ملف واحد وجميع ملفات JavaScript في ملف واحد كذلك. يؤدي دمج هذه الملفات ثم ضغطها إلى إنشاء طلب HTTP واحد صغير نسبيًا. <!-- سيء --> <link href="css/reset.css" rel="stylesheet"> <link href="css/base.css" rel="stylesheet"> <link href="css/site.css" rel="stylesheet"> <!-- جيد --> <link href="css/styles.css" rel="stylesheet"> يجب تحميل ملفات CSS في بداية صفحة الوِب ضمن الوسم head، أما ملفات JavaScript فيجب تحميلها في نهاية الصفحة ضمن الوسم body. والسبب هو أنّه يمكن استكمال تحميل ملفات CSS أثناء تحميل بقية أجزاء الصفحة، في حين أنّه لا يمكن تصيير ملفات JavaScript متعددة في نفس الوقت، لذا فإن هذه الملفات تمنع تحميل بقية عناصر الصفحة. يجب الانتباه هنا إلى ملفات JavaScript تُحمّل لا تزامنيًا Asynchronously بعد اكتمال تصيير الصفحة، وكذلك يجب الانتباه إلى أن JavaScript قد تكون مطلوبة في تصيير عناصر الصفحة كما هو الحال عند اسخدام HTML5 shiv. شرائح الصور Image Sprites المقصود بشرائح الصور في CSS هو استخدام صورة خلفية واحدة في مجموعة من العناصر، والهدف هنا هو تقليل عدد طلبات HTTP الناشئة من استخدام مجموعة من الصور. ولإنشاء شريحة اختر مجموعة من صور الخلفية الأكثر استخدامًا ورتّبها إلى جانب بعضها البعض في صورة واحدة، ثم باستخدام CSS أضف الشريحة كصورة خلفية إلى عنصر معين، ثم استخدم خاصّية background-position لعرض الصورة المطلوبة. بهذه الطريقة، يتم تمرير الصورة خلف العنصر ليقوم الأخير بعرض الجزء الملائم من الصورة. فعلى سبيل المثال إن كانت أبعاد العنصر هي 16 بكسل طولًا و 16 بكسل عرضًا فهذا يعني أنّ هذا العنصر سيعرض 16 بكسل طولًا و 16 بكسل عرضًا من الصورة، أما باقي أجزاء الصورة تكون مخفية. هذا مثال عن شريحة تضم صورًا لأيقونات قائمة محرر النصوص، وقد أحيطت كل صورة بخطوط إرشادية لتوضيح أماكن تغيّر موقع الخلفية. يمكن إنشاء قائمة باستخدام شريحة الصور السابقة وذلك بجعلها خلفية للعنصر span، ثم يمكن تغيير موضع الشريحة بواسطة الأصناف، وبذلك يمكن عرض الأيقونة المناسبة في كل مرة. HTML <ul> <li><a href="#"><span class="bold">Bold Text</span></a></li> <li><a href="#"><span class="italic">Italicize Text</span></a></li> <li><a href="#"><span class="underline">Underline Text</span></a></li> <li><a href="#"><span class="size">Size Text</span></a></li> <li><a href="#"><span class="bullet">Bullet Text</span></a></li> <li><a href="#"><span class="number">Number Text</span></a></li> <li><a href="#"><span class="quote">Quote Text</span></a></li> <li><a href="#"><span class="left">Left Align Text</span></a></li> <li><a href="#"><span class="center">Center Align Text</span></a></li> <li><a href="#"><span class="right">Right Align Text</span></a></li> </ul> CSS ul { margin: 0; padding: 0; } li { float: left; list-style: none; margin: 2px; } li a { background: linear-gradient(#fff, #eee); border: 1px solid #ccc; border-radius: 3px; display: block; padding: 3px; } li a:hover { border-color: #999; } li span { background: url("sprite.png") 0 0 no-repeat; color: transparent; display: block; font: 0/0 a; height: 16px; width: 16px; } .italic { background-position: -16px 0; } .underline { background-position: -32px 0; } .size { background-position: -48px 0; } .bullet { background-position: -64px 0; } .number { background-position: -80px 0; } .quote { background-position: -96px 0; } .left { background-position: -112px 0; } .center { background-position: -128px 0; } .right { background-position: -144px 0; } (تجربة حيّة) معرّف الموارد الموحّد URI الخاص ببيانات الصورة إضافة إلى استخدام شرائح الصور، يمكن الاستفادة من البيانات المُرمَّزة Encoded للصورة وتضمينها مباشرة في HTML و CSS وذلك من خلال معرّف الموارد الموحّد الخاص بالبيانات Data URI، وبهذا لن يُرسَل أي طلب HTTP إلى الخادوم. هذه الطريقة مفيدة للصور الصغيرة والتي لا تكون عرضة للتغيير، وعندما يكون بالإمكان نقل ملفات HTML و CSS إلى ذاكرة التخبئة Cach. ولكن لا يخلو الأمر من مشاكل، إذ يصعب أحيانا تبديل هذه المعرّفات ومتابعتها وقد تحتاج إلى توليد المعرّف مرة أخرى، إضافة إلى أنّها لا تعمل مع المتصفحات القديمة وخصوصًا Internet Explorer 7 وما قبله. إن كان بالإمكان تقليل عدد طلبات HTTP باستخدام هذه الطريقة، وإن كان بالإمكان تخزين ملفات HTML و CSS في ذاكرة التخبئة فإن الفوائد التي ستجينها باستخدام هذه الطريقة تفوق المشاكل المترتبة عنها. هناك عدد من الأدوات التي تساعد في توليد معرّف البيانات مثل هذا المحوّل وأداة Patternify. ومع ذلك تأكّد دائمًا من أنّ حجم البيانات في معرّف البيانات الخاصّ بالصورة أقل حجمًا من ملفّ الصورة الأصلية. HTML <img height="100" width="660" alt="Rigged Pattern" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAO6m73+X/bdxogujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQAyIyYy0nBAGgAAAABJRU5ErkJggg=="> CSS div { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAO6m73+X/bdxogujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQAyIyYy0nBAGgAAAABJRU5ErkJggg==") repeat; } (تجربة حيّة) خزّن الملفات الشائعة في ذاكرة التخبئة هناك طريقة أخرى لتقليل عدد طلبات HTTP المرسلة إلى الخادوم ولعرض الصفحات في وقت أقصر، وتتمثّل هذه الطريقة في إضافة الملفات الشائعة إلى ذاكرة التخبئة. فعند تحميل الصفحة للمرة الأولى يمكن تخزين ملفات معيّنة في ذاكرة التخبئة، وبهذا لن يحتاج المتصفح إلى طلب الملفات ذاتها - لمدة معيّنة - في كل زيارة لتلك الصفحة. تحديد مدّة بقاء هذه الملفات في ذاكرة التخبئة عائد إليك. وكما هو الحال مع ضغط الملفات، فإنّ تعيين مدّة بقاء الملفات في ذاكرة التخبئة يكون من خلال ملف .htaccess. ومرة أخرى فقد أعدّ فريق HTML5 Boilerplate ملفًّا خاصًّا لتعيين تاريخ انتهاء صلاحية الملفات في ذاكرة التخبئة وذلك في إعدادات خادوم Apache الخاصّ بهم. عادة ما تخزّن الصور ومقاطع الفيديو والخطوط وملفات الوسائط المعروفة لمدة شهر في ذاكرة التخبئة، أمّا ملفات CSS و JavaScript فتخزّن في الغالب لمدة عام كامل. وفي حال كانت ملفات CSS أو غيرها من الملفات عرضة للتغير في فترات متقاربة، يمكن تغيير اسم الملف - ومن الأفضل أن يكون الترقيم متسلسلًا - ليُحمَّل الملف من خلال المتصفح. كذلك يمكن تغيير مدة بقاء هذه الملفات في ذاكرة التخبئة إلى قيمة أقل. ExpiresByType text/css "access plus 1 year" ExpiresByType application/javascript "access plus 1 year" إن كانت ملفات CSS و JavaScript تتغيّر في كل أسبوع ولا يُتحكَّم في إصداراتها باستخدام ملفات منفصلة فمن الأفضل تغيير قيمة "access plus 1 year" إلى "access plus 1 week". ويمكن مراجعة صفحة صيغة mod_expires للتعرّف على القيم المتاحة. ترجمة - وبتصرّف - للمقال Performance & Organization لصاحبه Shay Howe. حقوق الصورة البارزة محفوظة لـ Freepik
  14. ماركداون هي صيغة نصّية صرفة Plain text format لكتابة المستندات بالاعتماد على صيغة تستخدم لتحديد هيئة النصّ وتنسيقه في الرسائل الإلكترونية والمدوّنات. طوّرت ماركداون على يد John Gruber سنة 2004، والذي كان أول شخص يكتب محوّلًا لماركداون إلى HTML بلغة Perl لينتشر حول العالم بعد فترة وجيزة. أدّى الانتشار الواسع الذي شهدته ماركدوان في السنوات التي تبعت إنشاء المحوّل الأول إلى تطوير العديد من المحوّلات وبلغات برمجية مختلفة. إضافة إلى ذلك توسّعت الصيغة الأصلية وأضيفت معايير جديدة لتنسيق الحواشي السفلية Footnotes والجداول والعناصر الأخرى المعروفة في المستندات، وظهرت كذلك أدوات تحويل تسمح بتحويل النصّ المكتوب بماركداون إلى تنسيقات أخرى غير HTML. أما اليوم فيعتمد الملايين من مستخدمي المواقع المشهورة مثل Reddit،StackOverflow و GitHub صيغة ماركداون للتواصل فيما بينهم، كما تدعم منصات التدوين الشهيرة مثل Wordpress و Drupal وغيرها هذه الصيغة في كتابة التدوينات. لم يعد استخدام هذه الصيغة مقتصرًا على الشبكة العنكبوتية وحسب، بل تجاوز ذلك إلى اعتمادها من قبل مؤلّفي الكتب وكتّاب المقالات ولإنشاء العروض التقديمية، ولكتابة الروايات والمحاضرات وغيرها. إنّ ما يميز صيغة ماركداون عن بقية الصياغات الترميزية المبسّطة والتي قد تكون أسهل في قواعدها من ماركداون، هو أن الأخيرة تمتاز بمقروئيتها العالية، فكما يصرّح Gruber: ومع أنّ الصيغة الأصلية لماركداون قد تأثّرت بصفة كبيرة بالكثير من مرشّحات تحويل النص إلى HTML مثل Setext و Textile و reStructuredText وغيرها إلّا أن مصدر الإلهام الرئيسي لـ Gruber كان هيئة النصوص الصرفة في الرسائل الإلكترونية. معايير صيغة ماركدوان في الواقع لم يعلن Gruber عن معايير قياسية واضحة للكتابة بصيغة ماركداون، ويرى البعض أنّ مبتكر هذه الصيغة قد تجاهلها ولم يعد يحاسب منتهكي حقوق الملكية، الأمر الذي تسبّب في ظهور صيغ وأشكال مختلفة من ماركداون. في العام 2012 تطوّع Jeff Atwood إلى جانب مجموعة من الأشخاص لوضع معايير قياسية للكتابة بصيغة ماركداون والتضمينات الأخرى لهذه الصيغة في اللغات البرمجية المختلفة، ولكن في العام 2014 اعترض Gruber على استخدام اسم Markdown لهذا المشروع، فتم تبديله إلى CommonMark. أصدر موقع CommonMark.org إصدارات مختلفة لمعايير ماركداون القياسية، ومن المؤمّل أن يعلن الموقع عن النسخة النهائية ذات الإصدار 1.0 من معايير ماركداون القياسية هذا العام. كذلك أعلنت GitHub عن نسختها الرسمية المعدّلة من ماركداون والتي تعتمد فيها على CommonMark باستثناء الجداول والروابط التلقائية Autolinks وقوائم المهام Task lists وخط الشطب Strikethrough، والتي أضافتها GitHub إلى المعايير القياسية. كتابة مستندات ماركداون تحمل مستندات ماركداون اللاحقتين .md و .markdown، ويمكن استخدام محررات النصوص المعروفة لإنشاء هذه الملفات وكتابتها. تتوفّر في الوقت الحاضر الكثير من المحرّرات الخاصة بهذه الصيغة، والتي تقدّم الكثير من الميزات المفيدة، مثل تلوين الرموز، وعرض المستند منسّقًا كما سيظهر على متصفح الإنترنت، وإنشاء ملف منسّق بصيغة PDF وغير ذلك الكثير. محرّرات ماركداون متوفّرة على جميع أنظمة التشغيل المعروفة، وهناك محرّرات مدفوعة، وأخرى مجّانية بعضها مفتوح المصدر وبعضها لا، وبعض المحرّرات يعمل على المتصفّح ويمكن استخدامه دون الحاجة إلى الاتصال بالإنترنت، ويكفي البحث في Google عن عبارة Markdown Editor لتجد أمامك مئات الخيارات المختلفة والمتنوّعة. الفقرات وفواصل الأسطر تعرّف الفقرة في ماركداون بأنّها مجموعة من الأسطر المتتابعة والمفصولة بسطر فارغ أو أكثر، والمقصود بالسطر الفارغ في ماركداون هو كل سطر يبدو للناظر فارغًا، وهذا يعني أنّ الأسطر التي تحتوي على عدد من المسافات الفارغة أو علامات الجدولة Tabs تعدّ أسطرًا فارغة. تحاط الفقرات بعد تحويلها إلى HTMl بالوسم <p></p>، أما في حال كنت ترغب في فصل فقرتين بواسطة الوسم <br/> فيمكنك إضافة مسافتين فارغتين في نهاية الفقرة وسيقوم مترجم ماركداون بالمطلوب. مثال: حسوب مجموعة من شركات الإنترنت تخدم المستخدمين العرب حول العالم. تدير حسوب أكبر منصتي عمل حر عربيتيْن وأكبر شبكة إعلانية في منطقة الشرق الأوسط وشمال أفريقيا، بالإضافة لمنتجات وشركات أخرى. توفّر حسوب حلولاً ومنتجات تساعد الشباب العرب على العمل والتطوّر وتتألّف من فريق عمل شاب وشغوف من مختلف الدول العربية. يترجم إلى: <p>حسوب مجموعة من شركات الإنترنت تخدم المستخدمين العرب حول العالم.</p> <p>تدير حسوب أكبر منصتي عمل حر عربيتيْن وأكبر شبكة إعلانية في منطقة الشرق الأوسط وشمال أفريقيا، بالإضافة لمنتجات وشركات أخرى.<br/> توفّر حسوب حلولاً ومنتجات تساعد الشباب العرب على العمل والتطوّر وتتألّف من فريق عمل شاب وشغوف من مختلف الدول العربية.</p> العناوين توفّر ماركداون صيغتين لتحديد العناوين ضمن النصوص: الصيغة الأولى هي نمط Setext، وتُحدَّد العناوين فيه من خلال وضع علامات المساواة (=) للعناوين من المستوى الأول، وعلامة الطرح (-) للمستوى الثاني، وليس هناك عدد محدد للعلامات تحت النص. هذا عنوان من المستوى الأول ======================== هذا عنوان من المستوى الثاني ------------------------- الصيغة الثانية هي نمط Atx، وتتيح تحديد العناوين حتى المستوى السادس، وذلك بوضع علامة (#) قبل نص العنوان، ويحدد عدد العلامات مستوى العنوان ضمن المتن. # هذا عنوان من المستوى الأول ### هذا عنوان من المستوى الثالث ###### هذا عنوان من المستوى السادس يترجم النص السابق إلى: <h1>هذا عنوان من المستوى الأول</h1> <h3>هذا عنوان من المستوى الثالث</h3> <h6>هذا عنوان من المستوى السادس</h6> الاقتباسات Blockqoutes تستخدم ماركداون الرمز (>) المستوحى من الرسائل الإلكترونية لتنسيق النصوص المقتبسة. بعد تحويل ملف ماركداون إلى HTML تحاط النصوص المسبوقة بهذا الرمز بوسمي <blockqoute>...</blockqoute>. > توفّر حسوب حلولاً ومنتجات تساعد الشباب العرب على العمل والتطوّر وتتألّف من فريق عمل شاب وشغوف من مختلف الدول العربية. يترجم إلى: <blockquote> <p>توفّر حسوب حلولاً ومنتجات تساعد الشباب العرب على العمل والتطوّر وتتألّف من فريق عمل شاب وشغوف من مختلف الدول العربية.</p> </blockquote> إذا كان الاقتباس مكوّنًا من عدة فقرات فيجب استخدام رمز الاقتباس في بداية كل فقرة، كذلك يمكن استخدام صياغات ماركداون المختلفة داخل النصوص المقتبسة ومن ضمنها النصوص المقتبسة ذاتها، وكما هو موضح في المثال التالي: > توفّر حسوب حلولاً ومنتجات تساعد الشباب العرب على العمل والتطوّر وتتألّف من فريق عمل شاب وشغوف من مختلف الدول العربية. > # عنوان من المستوى الأول > حسوب مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. تدير حسوب أكبر منصتي عمل حر عربية وأكبر شبكة إعلانية في منطقة الشرق الأوسط وشمال أفريقيا، بالإضافة لمنتجات وشركات أخرى. > > هذا اقتباس ضمن الاقتباس الأول القوائم تدعم ماركداون القوائم النقطية والرقمية، ويمكن استخدام النجمة (*) وعلامة الجمع (+) وعلامة الطرح (-) للتعبير عن القوائم النقطية: * أحمر * أخضر * أصفر - برتقال - تفاح - ليمون + إلكترون + بروتون + نيوترون تترجم القائمة النقطية إلى: <ul> <li>برتقال</li> <li>تفاح</li> <li>ليمون</li> </ul> أما القوائم الرقمية فتكتب بالصيغة التالية: تتألّف الذرات من الجسيمات التالية: 1. الإلكترونات 2. البروتونات 3. النيوترونات وتترجم إلى: <ol> <li>الإلكترونات</li> <li>البروتونات</li> <li>النيوترونات</li> </ol> من الجدير بالذكر أن محوّل ماركداون لا يعتمد على الأرقام المستخدمة في القائمة الرقمية عند التحويل إلى صيغة HTML، فلو كانت القائمة مكتوبة بالصيغة التالية: 1. برتقال 1. تفاح 1. ليمون أو بالصيغة التالية: 3. برتقال 9. تفاح 2. ليمون فإن النتيجة النهائية تكون واحدة. وفي حال تضمنت القائمة فقرات متعددة، يجب إزاحة السطر الأول من الفقرة إما بأربع مسافات فارغة أو بعلامة جدولة واحدة. * حسوب مجموعة من شركات الإنترنت تخدم المستخدمين العرب حول العالم. تدير حسوب أكبر منصتي عمل حر عربية وأكبر شبكة إعلانية في منطقة الشرق الأوسط وشمال أفريقيا، بالإضافة لمنتجات وشركات أخرى. * توفّر حسوب حلولاً ومنتجات تساعد الشباب العرب على العمل والتطوّر وتتألّف من فريق عمل شاب وشغوف من مختلف الدول العربية. يجدر الانتباه إلى أنّه يمكن الحصول على قائمة رقمية عن طريق الخطأ إذا ورد في المتن رقم متبوع بنقطة (مثلًا: انطلقت أكاديمية حسوب سنة 2015. وتضم الآن أكثر…) ولتجنب حدوث ذلك ضع خطًّا مائلًا عكسيًا بين الرقم والنقطة (انطلقت أكاديمية حسوب سنة 2015/. وتضم الآن أكثر…). الخطوط الأفقية الخطوط الأفقية في HTML تنتج من استخدام الوسم <hr/>، وفي ماركداون يمكن إدراج خط أفقي باستخدام النجمة (*) أو علامة الطرح (-)، أو الشَّرطَة السفلية (_)، بشرط إدراج ثلاثة رموز أكثر في سطر منفرد. في المثال التالي جميع الرموز ستعطي خطًّا أفقيًا بعد تحويل الملف إلى HTML. * * * *** ***** - - - --------------------------------------- التأكيد على الكلمات تستخدم ماركداون رمزيْ النجمة (*) والشرطة السفلية (_) للتأكيد على الكلمات. تؤدي إحاطة الكلمات المراد تأكيدها بنجمتيْن أو شرطتيْن إلى عرضها مائلة بعد تحويلها إلى HTML وذلك بإحاطتها بوسم <em>. أما إحاطة الكلمات بأربع نجمات أو شرطات فسيؤدي إلى عرضها بخط عريض Bold وذلك بإحاطتها بوسم <strong>. أما إحاطة الكلمات بست نجمات أو شرطات فسيؤدي إلى إحاطتها بالوسمين معًا. *حسوب* مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. _حسوب_ مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. **حسوب** مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. __حسوب__ مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. _*حسوب*_ مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. ***حسوب*** مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. ___حسوب___ مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم. النتيجة بعد التحويل إلى HTML: <p><em>حسوب</em> مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم.</p> <p><strong>حسوب</strong> مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم.</p> <p><strong><em>حسوب</em></strong> مجموعة من شركات الانترنت تخدم المستخدمين العرب حول العالم.</p> الروابط Links توفّر ماركداون أسلوبيْن لكتابة الروابط، التضمين Inline والإشارة Reference، وفي كلا النمطين يوضع نص الرابط بين قوسين معقوفين []. في حالة الرابط الضمني، يُضاف عنوان الرابط بين قوسين () بعد القوسيْن المعقوفين بالصورة التالية: هذا [مثال على رابط](http://www.example.com) ضمني في ماركداون. والنتيجة: هذا <a href="http://www.example.com">مثال على رابط</a> ضمني في ماركداون. أما أسلوب الإشارة فيكون بالصيغة التالية: هذا [مثال على رابط][id] بأسلوب الإشارة في ماركداون. يمكن الآن تعريف عنوان الرابط في أي مكان ضمن المستند، وبالصيغة التالية: [id]: http://www.example.com يمكن استخدام الحروف والأرقام والرموز في تحديد معرّف الرابط، وعند إجراء عملية التحويل إلى HTML يربط المحوّل بين المعرّف وبين نص الرابط ثم يحذف الإشارات من المستند. يمكن كذلك استخدام نص الرابط كمعرّف للرابط، وذلك بكتابة نص الرابط بالطريقة التالية: [Google][] ثم تعريف معرّف الرابط: [Google]: http://google.com الصور يمكن إضافة الصور إلى المستند باستخدام ماركداون وبنفس الأسلوب المتبع في إضافة الروابط (التضمين والإشارة)، مع فارق وحيد، وهو إضافة علامة التعجب (!) قبل القوسين المعقوفين: ![[جبال الألب](images/alps.jpg) ![جبال الألب][id] [id]: images/alps.jpg تجدر الإشارة إلى أنّ ماركداون لا تقدّم صيغة لتحديد طول وعرض الصورة؛ لذا إن كنت بحاجة إلى القيام بذلك يمكنك استخدام وسم <img> الاعتيادي ضمن مستند ماركداون. النصوص البرمجية إن كنت بحاجة إلى إضافة بعض النصوص البرمجية إلى المستند الخاصّ بك، فإن ماركداون تقدّم أسلوبين لتنسيق هذه النصوص، ضمن السطر أو على هيئة كتلة نصية مستقلة. يمكن الاستفادة من الأسلوب الأول عندما ترغب في سرد النصوص البرمجية القصيرة داخل المتن: تستخدم الدالة `alert()` لعرض الرسائل التنبيهية. يحيط مترجم ماركداون الشيفرة البرمجية بوسم <code> كما يلي: <p>تستخدم الدالة <code>alert()</code> لعرض الرسائل التنبيهية.</p> أما إن كنت ترغب في عرض كتلة برمجية كبيرة الحجم فيمكنك اتباع الأسلوب الثاني، وذلك بإضافة أربع مسافات فارغة أو علامة جدولة في بداية كل سطر من أسطر الشيفرة البرمجية. هذه فقرة عادية. هذه فقرة تتضمن كتلة نصية برمجية ومن الواضح أن الكتلة البرمجية تمتد إلى عدة أسطر والنتيجة: <p>هذه فقرة عادية.</p> <pre><code>هذه فقرة تتضمن كتلة نصية برمجية ومن الواضح أن الكتلة البرمجية تمتد إلى عدة أسطر </code></pre> بقي أن نشير إلى أنّه يمكن استخدام كافّة وسوم HTML في مستند ماركداون، بشرط أن يسبق الوسمَ سطرٌ فارغ. على سبيل المثال لإضافة جدول إلى مستند ماركداون يمكن استخدام وسم <table> كما يلي: هذه فقرة عادية. <table> <tr> <td>هذا النص في جدول</td> </tr> </table> فقرة عادية أرى. المصادر: موقع مبتكر صيغة ماركداون CommonMark Wikipedia
  15. إن قدرتك على كتابة شفرة HTML وCSS مع فهم عميق لما تكتبه يعني امتلاكك لمهارة كبيرة ومتميّزة، وعند ازدياد حجم شفرات الموقع الإلكتروني وكذلك عدد زوّاره، تظهر الحاجة إلى مجموعة جديدة من المهارات المهمّة والضرورية لتوفير الوقت أثناء التطوير ولتحسين تجربة المستخدم؛ لذا فإن معرفة أساسيات تحسين أداء الموقع الإلكتروني وتنظيمه قد يساعد كثيرًا في هذا الصدد. تؤثّر طريقة تنظيم الشفرة البرمجية وأسلوب بنائها كثيرًا على سرعة التطوير، إضافة إلى سرعة تصيير Rendering الصفحة على متصفح الإنترنت، وقد يشغل هذا الأمران اهتمام المطورين والمستخدمين على حد سواء. تمكن زيادة سرعة الإنتاج وتوفير تجربة أفضل للمستخدمين من خلال قضاء الوقت الكافي في التخطيط للحصول على بنية صحيحة للشفرة الأساسية، وتحديد آلية عمل المكونات المختلفة مع بعضها. إستراتيجية وبنية الشفرة الأساسية تتمثّل أول خطوة في طريق تحسين أداء الموقع الإلكتروني وتنظيمه في تحديد إستراتيجة وبنية جيدة للشفرة الأساسية، وذلك من خلال ترتيب مجلدات المشروع بصورة جيدة، وتحديد أنماط التصميم Design patterns، وإيجاد السبل اللازمة لإعادة استخدام الشفرة الشائعة. بنية الأنماط Styles إن طريقة تنظيم الأنماط أمر عائد إلى الرغبة الشخصية وطبيعة الموقع الإلكتروني المراد إنشاؤه، ولكن - عمومًا - هناك بعض الممارسات الجيدة التي ينصح باتباعها، وإحدى هذه الممارسات هي فصل الأنماط بناء على الغرض من استخدامها، وهذا يعني إنشاء مجلدات للأنماط الأساسية الشائعة، ولمكونات واجهة الاستخدام، ووحدات منطق العمل Business logic modules. # Base – normalize.css – layout.css – typography.css # Components – alerts.css – buttons.css – forms.css – list.css – nav.css – tables.css # Modules – aside.css – footer.css – header.css تتضمن بنية الملفات الموضّحة أعلاه ثلاثة مجلدات تضمّ مجموعات فريدة من الأنماط، والهدف هنا هو أن تبدأ بالنظر إلى المواقع الإلكترونية على أنّها أنظمة وليست صفحات منفردة، ويجب أن تعكس بنية الشفرة البرمجية هذا المبدأ. لاحظ عدم وجود أي أنماط خاصّة بالصفحات في بنية الملفات هذه. يتضمن المجلد base الأنماط الشائعة والمتغيّراات المستخدمة في أرجاء الموقع الإلكتروني كالأنماط المتعلقة بمخطط الصفحات وتنسيق النصوص على سبيل المثال. أما المجلد components (المكوّنات) فيتضمن الأنماط الخاصة بعناصر واجهة الاستخدام والتي يمكن تقسيمها إلى مكونات مختلفة مثل التنبيهات والأيقونات. أخيرًا، يتضمن المجلد modules (الوحدات) الأنماط الخاصة بأقسام الصفحة المختلفة، والتي تُحدَّد حسب طبيعة المشروع واحتياجاته. لا ترتبط أنماط المكونات بأي شكل من الأشكال بمنطق العمل الجوهري للموقع الإلكتروني وإنما تعتمد على الواجهة بصورة تامة، أما الأنماط الخاصة بمنطق العمل فتكون موجودة في الوحدات ، ومن الشائع استخدام عددٍ من المكونات الخاصة بواجهة الاستخدام ضمن هذه الوحدات. فعلى سبيل المثال يمكن أن يحتوي العمود الجانبي في الصفحة على أنماط القوائم والأيقونات المعرّفة ضمن أنماط المكونات في حين تكون بعض الأنماط الأخرى متوارثة من نمط الوحدة. يساعد فصل الأنماط بهذه الطريقة على تكوين أنماط مسبقة متقنة الصنع والقدرة على استخدام الأنماط في أماكن متعددة من الموقع الإلكتروني وإعادة استخدامها حسب الحاجة. إن ترتيب الأنماط بهذه الأسلوب ليس أمر مستحدثًا، وقد ورد ذكره سابقًا في عدد من منهجيات CSS مثل CSS كائنية التوجه Object Oriented CSS أو OOCSS اختصارًا، وبنية CSS القابلة للتجميع والتوسع Scalable and Modular Architecture for CSS أو SMACSS اختصارًا. لكل واحدة من هذه المنهجيات آراؤها الخاصة حول بنية الملفات وكذلك حول طريقة استخدام الأنماط. CSS كائنية التوجه Nicole Sullivan واحدة من روّاد منهجية CSS كائنية التوجه وذلك في عملها المتمثّل في كتابة الأنماط الخاصة بالمواقع الإلكترونية الكبيرة. تضع هذه المنهجية مبدأين أساسيين يساعدان على بناء مواقع إلكترونية قابلة للتوسع تعتمد على بنية قوية ومتماسكة وبمقدار معقول من الشفرة البرمجية. وهذا المبدآن هما: فصل البنية عن القشرة، فصل المحتوى عن الحاوي. المقصود بفصل البنية عن القشرة هو تجريد مخطط العنصر من السمة العامة للموقع الإلكتروني. بمعنى أنّه يجب أن تكون بنية الوحدة شفافة، وتسمح بتوارث الأنماط الأخرى وعرضها دون أي تضارب. ويتطلب هذا بنية رصينة للمخطّطات والشبكات إلى جانب وحدات متقنة الصنع. أما فصل المحتوى عن الحاوي فيعني عدم اعتماد العناصر الأبناء على العنصر الأب، فعلى سبيل المثال يجب أن يظهر العنوان بنفس المظهر بغض النظر عن العنصر الأب الذي يحتويه. ولتحقيق هذا، يجب أن ترث العناصر أنماطًا مبدئية، ثم تُوسَّع هذه الأنماط حسب الحاجة. HTML <div class="alert alert-error"> <p class="msg">...</p> </div> CSS .alert {...} .alert-error {...} .msg {...} تشجّع CSS كائنية التوجه على بناء مكتبة للمكونات، والتحلّي بالمرونة، والاستفادة من الشبكة. تشكّل هذه القواعد أرضية جيدة يمكن أن تساعد في تجنّب الحاجة إلى إنشاء أنماط إضافية عند إضافة صفحات أو خصائص جديدة للموقع الإلكتروني. بنية CSS القابلة للتجميع والتوسع المنهجية الثانية في تنظيم شفرة CSS هي المنهجية التي طوّرها Jonathan Snook تحت اسم بنية CSS القابلة للتجميع والتوسع. تدعو هذه المنهجية إلى تقسيم الأنماط إلى خمسة فئات رئيسية، هي: الأساس Base، المخطط Layout، الوحدة Module، الحالة State، السمة Theme. تضمّ فئة الأساس الأنماط الأساسية للعناصر التي تغطي الأمور العامة والمبدئية، ثم تأتي بعدها فئة المخطط لتحديد الأحجام المختلفة للعناصر إلى جانب أنماط الشبكة مانحة إياها المخطط الرئيسي. أما أنماط الوحدة فهي أنماط مخصصة بصورة أكبر وهي موجّهة إلى أجزاء محددة من الصفحة، كروابط التنقّل في الموقع (navigation) أو الأنماط المميزة الأخرى، ثم تأتي بعدها أنماط الحالة التي تستخدم لزيادة أو تعديل أنماط أخرى في حال تضمنت الوحدة المعنية حالات مختلفة، كلسان تبويب مفعّل مثلًا. وأخيرًا، يمكن إضافة فئة السمة والتي تتضمن أنماط تستند إلى المظهر الخارجي للوحدات المختلفة. HTML <div class="alert is-error"> <p>...</p> </div> CSS .alert {...} .alert.is-error {...} .alert p {...} .alert.is-error p {...} في المثال السابق يكون الصنف alert ضمن فئة الوحدة في حين أن الصنف is-error يكون ضمن فئة الحالة. بعد ذلك تُتوارث الأصناف من هذه الفئات حسب الحاجة. أي منهجية ستختار؟ اختيار المنهجية التي ستستخدمها في عملك أمر عائد إليك تمامًا، وقد تشعر بأنّ منهجية معينة تناسب مشروعًا معينًا والعكس صحيح. بصورة عامة، لا بأس باستخدام مزيج من المنهجيتين والاستفادة من مبادئهما حسب ما تقتضيه الحاجة. تحسين الأداء بواسطة المحدّدات Selectors عدم الاهتمام بالمحدّدات من الأمور التي يُبتلى بها الكثير من المطوّرين، حيث ينصبّ جلّ الاهتمام في الغالب على الخصائص والقيم التابعة لها، فما دامت الأنماط مطبّقة على العنصر الصحيح فإن كل شيء يبدو على ما يرام. ولكن هذا افتراض خاطئ تمامًا، فلطريقة تحديد العناصر في CSS أثرٌ كبير على الأداء، بما في ذلك سرعة عرض الصفحات ومدى فعالية الأنماط في البنية العامة للموقع. اجعل المحدّدات قصيرة قدر الإمكان هناك الكثير من الفوائد الناجمة عن تقصير محدّدات CSS قدر الإمكان، إذ بتقصيرها تقل التخصّصية، ما يسمح بتوارث وتناقل أفضل للأنماط، وزيادة في كفاءتها. أما المحدّدات الطويلة فتقلّل الأداء لأنها تجبر المتصفح على تصيير كل نوع من أنواع المحددات على حدة من اليمين إلى اليسار، كما تشكّل عائقًا أمام المحدّدات الأخرى لتكون أكثر تخصّصًا. /* سيء */ header nav ul li a {...} /* جيد */ .primary-link {...} /* سيء */ button strong span {...} button strong span .callout {...} /* جيد */ button span {...} button .callout {...} المحدّد الأول في الشفرة السابقة مخصّص جدًّا، ويمكن تحديده وتصييره بسرعة أكبر باستخدام الصنف. إضافة إلى ذلك، فإن استخدام الصنف في هذه الحالة يقلل بقدر ملحوظ من الحاجة إلى تحديد العنصر الأب، الأمر الذي يسمح بتغيير موقع العنصر فيما بعد دون إفساد الأنماط الأخرى. أما المثال الثاني فيتضمن محدّدات أقصر من المثال الأول ولكن يمكن تحسينها من خلال توفير المستوى ذاته من التخصصية لكل محدد. لا تبالغ في استخدام محددات مخصّصة جدًّا وبهذا ستواجه مشاكل أقلّ بكثير في حال حدوث تغير في ترتيب عناصر الصفحة. إن التقليل من عدد المحدّدات وإعطائها جميعًا القوة ذاتها سيمكّنها من العمل مع بعضها البعض بصورة أفضل. إن الهدف الأساسي من تقصير المحدّدات هو تقليل التخصصية وكتابة شفرة أوضح وأكثر ترتيبًا. استخدم الأصناف Classes دائمًا الأصناف رائعة جدًّا، إذ يصيّرها المتصفح بسرعة كما أنّها تتيح إعادة استخدام الأنماط إلى جانب استخدامها الواسع في بناء المواقع الإلكترونية. ولكن عند استخدام الأصناف يجب الانتباه إلى ممارسات مجرَّبة ستساعد على تحقيق أقصى فائدة ممكنة منها. تُصيَّر المحدّدات من اليمين إلى اليسار؛ لذا فمن الضروري أن تعتني جيّدًا بـ المحدّد الرئيسي Key selector وهو المحدد الأخير ضمن قائمة المحدّدات من الجهة اليمنى، وهو محدّد مهمّ للغاية؛ لأنّه يحدّد العنصر الأول الذي سيعثر عليه المتصفح أثناء بحثه ضمن الصفحة، وسيؤدي عدم الاعتناء بالمحدد الرئيسي إلى دخول المتصفح في فوضى عارمة. لا تتردّد في استخدام صنف فريد من أجل تحقيق الفائدة بالنسبة لأداء الصفحة، كذلك لا تُضِف أي عنصر قبل محدّدات الأصناف، فذلك سيمنعك من تطبيق هذه الأنماط على عناصر أخرى، وسيزيد من مقدار الخصوصية التي يتمتع بها ذلك المحدد. /* سيء */ #container header nav {...} /* جيد */ .primary-nav {...} /* سيء */ article.feat-post {...} /* جيد */ .feat-post {...} يجدر بك كذلك الابتعاد عن استخدام محدّدات المعرّفات ID selectors قدر الإمكان؛ ذلك لأنّها محدّدات مخصّصة جدًّا ولا تسمح بإعادة استخدامها في أي مكان آخر، ويسعني القول إنّ استخدام المعرّف لا يختلف كثيرًا عن استخدام !important. الشفرة القابلة لإعادة الاستخدام إن أحجام الملفات الكبيرة وعمليات التصيير غير الضرورية التي يقوم بها المتصفح هي من أكبر العوائق التي تقف في وجه الأداء الجيد. وإعادة استخدام الأنماط قدر الإمكان من أسرع الطرق في تقليل حجم ملفات CSS، إذ يجب دمج أنماط الواجهة Interface patterns والأنماط المكررة ليكون بالإمكان مشاركة الشفرة ذاتها على عدد من العناصر. فعلى سبيل المثال لو كانت هناك وحدتان تتشاركان في عدد من الخصائص (كلون الخلفية، والأركان الدائرية، والظل) فلا حاجة على الإطلاق للتصريح عن النمط ذاته مرتين، بل يمكن دمج هذه الخصائص في صنف واحد، وبهذا تكتب الأنماط مرة واحدة ويكون بالإمكان مشاركتها في أماكن مختلفة. ولكن يجب الانتباه إلى أنّ إعادة استخدام الشفرة لا يكون على حساب الدلالية Semantics. يمكن في هذه الحالة استخدام زوج من المحددات وفصلهما بفاصلة، وبهذا يمكن للأنماط أن تكون متوارثة بين المحدّدين في الوقت ذاته. هناك طريقة أخرى غالبًا ما نراها في منهجيتيْ OOCSS و SMACSS اللتين تحدّثنا عنهما سابقًا، وتتلخّص هذه الطريقة في تعريف الأنماط في صنف واحد، ثم تطبيق أصناف متعددة على العنصر ذاته. /* سيء */ .news { background: #eee; border-radius: 5px; box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25); } .social { background: #eee; border-radius: 5px; box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25); } /* جيد */ .news, .social { background: #eee; border-radius: 5px; box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25); } /* الأفضل */ .modal { background: #eee; border-radius: 5px; box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25); } ليس هناك فارق كبير بين الطريقتين ما دامت الشفرة مشتركة وقابلة لإعادة الاستخدام، وما دام الحجم النهائي للملف صغيرًا. الخطوة المواليّة لتنظيم شفرة CSS هي معرفة أساسيّات تحسين أداء الموقع. ترجمة - وبتصرّف - للمقال Performance & Organization لصاحبه Shay Howe. حقوق الصورة البارزة محفوظة لـ Freepik