الفرق بين المراجعتين لصفحة: «Rails/active record validations»

من موسوعة حسوب
أنشأ الصفحة ب'يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال مي...'
 
طلا ملخص تعديل
 
(4 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة)
سطر 1: سطر 1:
يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال ميزة التحققات من الصحة (validations) التي يوفرها السجل الفعَّال. بعد قراءة هذا الدليل، ستتعرَّف على:
<noinclude>{{DISPLAYTITLE:عمليات التحقق من Active Record في ريلز}}</noinclude>
* كيفية استعمال مساعدي التحقق من الصحة (validation helpers) للسجل الفعَّال المدمجين.
[[تصنيف:Rails]]
[[تصنيف:Rails Models]]
يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال ميزة التحققات من الصحة (validations) التي يوفرها [[Rails/active record|Active Record]]. بعد قراءة هذا الدليل، ستتعرَّف على:
* كيفية استعمال مساعدي التحقق من الصحة (validation helpers) لـ [[Rails/active record|Active Record]] المدمجين.
* كيفية إنشاء توابع مخصصة للتحقق من الصحة.
* كيفية إنشاء توابع مخصصة للتحقق من الصحة.
* كيفية العمل مع رسالة الخطأ المولدة عبر عملية التحقق.
* كيفية العمل مع رسالة الخطأ المولدة عبر عملية التحقق.
سطر 25: سطر 28:
يمكن استخدام هذه الطرق في حالات مخصصة. برأي فريق عمل ريلز، طريقة التحقُّقات في مستوى النماذج (model-level validations) هي الطريقة المثلى في معظم الحالات.
يمكن استخدام هذه الطرق في حالات مخصصة. برأي فريق عمل ريلز، طريقة التحقُّقات في مستوى النماذج (model-level validations) هي الطريقة المثلى في معظم الحالات.


=== متى يحدث التحقُّق؟ ===
=== متى يحدث التحقق؟ ===
هناك نوعان من كائنات [[Rails/active record basics|السجل الفعال]]: الكائنات التي تتفاعل مع سجل من قاعدة بياناتك، والكائنات التي لا تتفاعل. عند إنشائك لكائن جديد، مثلًا باستخدام التابع <code>new</code>، لن يتم حفظ هذا الكائن في قاعدة البيانات بعد. بعد استدعائك للتابع <code>save</code> على هذا الكائن، سيتم حفظه في جدول قاعدة البيانات. يستخدم السجل الفعال تابع النسخة <code>?new_record</code> للتأكّد من وجود السجل في قاعدة البيانات أم لا. لنطلع على نموذج السجل الفعال البسيط التالي:<syntaxhighlight lang="rails">
هناك نوعان من كائنات [[Rails/active record basics|Active Record]]: الكائنات التي تتفاعل مع سجل من قاعدة بياناتك، والكائنات التي لا تتفاعل. عند إنشائك لكائن جديد، مثلًا باستخدام التابع <code>new</code>، لن يتم حفظ هذا الكائن في قاعدة البيانات بعد. بعد استدعائك للتابع <code>save</code> على هذا الكائن، سيتم حفظه في جدول قاعدة البيانات. يستخدم [[Rails/active record|Active Record]] تابع النسخة <code>?new_record</code> للتأكّد من وجود السجل في قاعدة البيانات أم لا. لنطلع على نموذج [[Rails/active record|Active Record]] البسيط التالي:<syntaxhighlight lang="rails">
class Person < ApplicationRecord
class Person < ApplicationRecord
end
end
سطر 50: سطر 53:
* <code>update</code>
* <code>update</code>
* <code>update!‎</code>
* <code>update!‎</code>
ترمي التوابع ذات الإصدار الصاخب (bang version مثل التابع <code>!save</code>) استثناءات عند عدم صحة الكائنات. بينما لا تفعل ذلك التوابع ذات الإصدار الصامت (أو غير الصاخبة [non-bang versions]) مثل التابعان <code>save</code> و <code>update</code> اللذان يعيدان القيمة <code>false</code>، والتابع <code>create</code> يعيد الكائن نفسه.
ترمي التوابع ذات الإصدار المُغيِّر (bang version مثل التابع <code>!save</code>) استثناءات عند عدم صحة الكائنات. بينما لا تفعل ذلك التوابع ذات الإصدار غير المُغيِّر (non-bang versions) مثل التابعان <code>save</code> و <code>update</code> اللذان يعيدان القيمة <code>false</code>، والتابع <code>create</code> يعيد الكائن نفسه.


=== تجاوز التحقُّقات ===
=== تجاوز التحقُّقات ===
سطر 69: سطر 72:


=== التابعان <code>?valid</code> و <code>?invalid</code> ===
=== التابعان <code>?valid</code> و <code>?invalid</code> ===
قبل حفظ كائن سجل فعال، يشغّل ريلز التحقُّقات الخاصة بك. في حال أثمرت هذه التحقُّقات أخطاءً، فلن يحفظ ريزل الكائن.
قبل حفظ كائن [[Rails/active record|Active Record]]، يشغّل ريلز التحقُّقات الخاصة بك. في حال أثمرت هذه التحقُّقات أخطاءً، فلن يحفظ ريزل الكائن.


يمكنك أيضًا تشغيل التحقُّقات يدويًا. إذ يقوم التابع <code>?valid</code> بتشغيل التحقق ويعيد القيمة <code>true</code> في حال نجاحه، والقيمة <code>false</code> عدا عن ذلك. كما رأيت أعلاه:<syntaxhighlight lang="rails">
يمكنك أيضًا تشغيل التحقُّقات يدويًا. إذ يقوم التابع <code>?valid</code> بتشغيل التحقق ويعيد القيمة <code>true</code> في حال نجاحه، والقيمة <code>false</code> عدا عن ذلك. كما رأيت أعلاه:<syntaxhighlight lang="rails">
سطر 78: سطر 81:
Person.create(name: "John Doe").valid? # => true
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false
Person.create(name: nil).valid? # => false
</syntaxhighlight>بعد قيام كائن السجل الفعال بالتحقُّقات، يمكن الوصول لرسائل الخطأ عن طريق تابع النسخة <code>errors.messages</code>، الذي يعيد مجموعة من الأخطاء. من التعريف، يكون الكائن صحيحًا في حال كانت هذه المجموعة خالية بعد تنفيذ التحقُّق.
</syntaxhighlight>بعد قيام كائن [[Rails/active record|Active Record]] بالتحقُّقات، يمكن الوصول لرسائل الخطأ عن طريق تابع النسخة <code>errors.messages</code>، الذي يعيد مجموعة من الأخطاء. من التعريف، يكون الكائن صحيحًا في حال كانت هذه المجموعة خالية بعد تنفيذ التحقُّق.


تجدر الملاحظة أنَّ الكائن الذي يتم تهيئته باستخدام <code>new</code> لن يعطي أي خطأ حتّى لو كان غير صالح، لأنَّ التحقُّقات تنفّذ تلقائيًا عند حفظ الكائن، كما هو الحال في التابعين <code>create</code> و <code>save</code>.<syntaxhighlight lang="rails">
تجدر الملاحظة أنَّ الكائن الذي يتم تهيئته باستخدام <code>new</code> لن يعطي أي خطأ حتّى لو كان غير صالح، لأنَّ التحقُّقات تنفّذ تلقائيًا عند حفظ الكائن، كما هو الحال في التابعين <code>create</code> و <code>save</code>.<syntaxhighlight lang="rails">
سطر 134: سطر 137:
</syntaxhighlight>إن استخدام <code>details</code> مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات.
</syntaxhighlight>إن استخدام <code>details</code> مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات.


== مساعدات التحقُّق ==
== مساعدو التحقق ==
يزوّد السجل الفعال بمجموعة معرفة مسبقًا من المساعدات التي يمكنك استخدامها بشكل مباشر في تعريفات الأصناف الخاصة بك. تزوّد هذه المساعدات بقواعد متعارف عليها في التحقُّقات. في كل مرة يفشل فيها تحقُّق ما، سيتم إضافة رسالة خطأ إلى المجموعة errors الخاصة بالكائن، وتضم هذه الرسالة اسم الحقل الذي فشل عليه التحقُّق.
يزوّد [[Rails/active record|Active Record]] مجموعةً معرفةً مسبقًا من المساعدين الذين يمكنك استخدامهم بشكل مباشر في تعريفات الأصناف الخاصة بك. تزوّد هؤلاء المساعدين بقواعد متعارف عليها لإجراء التحقُّقات. في كل مرة يفشل فيها تحقُّق ما، سيتم إضافة رسالة خطأ إلى المجموعة <code>errors</code> الخاصة بالكائن، وتضم هذه الرسالة اسم الحقل الذي فشل التحقُّق منه.


يقبل كل مساعد مجموعة من أسماء الحقول، أي في سطر تعريف وحيد يمكنك إضافة نفس التحقُّق لمجموعة من الحقول.
يقبل كل مساعد مجموعة من أسماء الحقول، أي يمكنك في سطر تعريف وحيد إضافة نفس التحقُّق لمجموعة من الحقول.


جميعها تقبل الخيارات on: و message:، التي تحدد متى يتم تشغيل التحقُّقات وما الرسالة الواجب إضافتها في حال فشل التحقُّق. يقبل الخيار on: أحد القيمتين create: أو update:. هناك رسائل افتراضية لكل نوع من أنواع التحقُّقات، والتي يتم استخدامها عند عدم تحديد رسالة بشكل يدوي في الخيار message:. لنطلّع على كل مساعد من هذه المساعدات.
جميعهم يقبلون الخيارين <code>on:</code> و <code>message:</code>، اللذَين يحددان متى يتم تشغيل التحقُّقات وما الرسالة الواجب إضافتها في حال فشل التحقُّق. يقبل الخيار <code>on:</code> إحدى القيمتين <code>create:</code> أو <code>update:</code>. هناك رسائل افتراضية لكل نوع من أنواع التحقُّقات، والتي يتم استخدامها عند عدم تحديد رسالة بشكل يدوي في الخيار <code>message:</code>. لنطلّع على كل مساعد من هؤلاء المساعدين.
 
=== مساعد القبول acceptance ===
يأكّد هذا المساعد أن صندوق تحديد checkbox تم تحديده على واجهة المستخدم عند إرسال النموذج. يستخدم هذا المساعد بشكل تقليدي عند حاجة المستخدم للموافقة على شروط استخدام التطبيق، أو للتأكد من قراءة نص معين، أو أي مفهوم مماثل.


=== المساعد <code>acceptance</code> ===
يأكّد هذا المساعد أن صندوق الاختيار (checkbox) تم تحديده على واجهة المستخدم عند إرسال النموذج. يستخدم هذا المساعد بشكل تقليدي عند حاجة المستخدم للموافقة على شروط استخدام التطبيق مثلَا، أو للتأكد من قراءة نص معين، أو أي شيء مماثل.<syntaxhighlight lang="rails">
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :terms_of_service, acceptance: true
 validates :terms_of_service, acceptance: true
 
end
end
 
</syntaxhighlight>يُنفّذ هذا التحديد فقط عندما يكون <code>terms_of_service</code> ليس <code>nil</code>. إن رسالة الخطأ الافتراضية لهذا المساعد هي "must be accepted". يمكنك تمرير رسالة يدوية عن طريق الخيار <code>message:</code>.<syntaxhighlight lang="rails">
يُنفّذ هذا التحديد فقط عندما يكون terms_of_service ليس nil. إن رسالة الخطأ الافتراضية لهذا المساعد هي "must be accepted". يمكنك تمرير رسالة يدوية عن طريق الخيار message:.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :terms_of_service, acceptance: { message: 'must be abided' }
 validates :terms_of_service, acceptance: { message: 'must be abided' }
 
end
end
 
</syntaxhighlight>يمكن أيضًا تمرير الخيار <code>accept:</code> الذي يحدد القيم التي يجب اعتبارها خيارًا مقبولًا. القيمة الافتراضية لهذا الخيار هي <code>[1,true]</code> ويمكن تغييرها بسهولة:<syntaxhighlight lang="rails">
يمكن أيضًا تمرير الخيار accept: الذي يحدد القيم التي يجب اعتبارها كالخيار المقبول. القيمة الافتراضية لهذا الخيار هي [1,true] ويمكن تغييرها بسهولة:
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :terms_of_service, acceptance: { accept: 'yes' }
 validates :terms_of_service, acceptance: { accept: 'yes' }
  validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
 
<nowiki> validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }</nowiki>
 
end
end
</syntaxhighlight>هذا التحقُّق مخصَّص لتطبيقات الويب وليس من الضروري تخزين هذا القبول في أي مكان في قاعدة البيانات. في حال لم تكن تملك هذا الحقل في قاعدة البيانات، يقوم المساعد بإنشائه لك بشكل افتراضي. في حال عدم وجود الحقل في قاعدة البيانات، يجب ضبط الخيار <code>accept</code> أو أن يحوي القيمة <code>true</code>، وإلا لن يتم تشغيل التحقُّق.


يعتبر هذا التحقُّق حصري بمواقع الويب وليس من الضروري تخزين هذا القبول في أي مكان في قاعدة البيانات. في حال لم تكن تملك هذا الحقل في قاعدة البيانات، يقوم المساعد بإنشائه بشكل افتراضي. في حال عدم وجود الحقل في قاعدة البيانات، يجب تحديد الخيار accept أو أن يحوي القيمة true، وإلا لن يتم تشغيل التحقُّق.
=== المساعد <code>validates_associated</code> ===
 
يمكنك استخدام هذا المساعد في حال كان نموذجك يملك ارتباطات مع نماذج أخرى تحتاج للتحقُّق أيضًا. عند محاولتك حفظ السجل، سيتم استدعاء التابع <code>?valid</code> على كل كائن من الكائنات المرتبطة أيضًا.<syntaxhighlight lang="rails">
=== مساعد الارتباط validates_associated ===
يمكنك استخدام هذا المساعد في حال كان نموذج يملك ارتباطات مع نماذج أخرى تحتاج للتحقُّق أيضًا. عند محاولتك لحفظ السجل، سيتم مناداة التابع ?valid على كل كائن من الكائنات المرتبطة أيضًا.
 
class Library < ApplicationRecord
class Library < ApplicationRecord
 
  has_many :books
 has_many :books
  validates_associated :books
 
 validates_associated :books
 
end
end
</syntaxhighlight>سيعمل هذا التحقُّق على جميع الأنواع المرتبطة.


سيعمل هذا التحقُّق على جميع الأنواع المرتبطة.
'''تحذير''': لا تستخدم المساعد <code>validates_associated</code> على طرفي الارتباط، إذ سيؤدي ذلك إلى استدعاء أحدهما للآخر مما يؤدي إلى الدخول في حلقة لا نهائية.
 
ملاحظة: لا تقم باستخدام validates_associated على طرفي الارتباط. إذ سيؤدي ذلك إلى مناداة بعضها البعض مما يؤدي إلى حلقة لا نهائية.


إن رسالة الخطأ الافتراضية لهذا التحقُّق هي "is invalid". تجب الملاحظة أن لكل كائن من الكائنات المرتبطة مجموعة الأخطاء الخاصة به، ولن يتم حفظ مجموعة الأخطاء للكائنات الأبناء في الكائن المستدعي.
إن رسالة الخطأ الافتراضية لهذا التحقُّق هي "is invalid". تجب الملاحظة أن لكل كائن من الكائنات المرتبطة مجموعة الأخطاء الخاصة به، ولن يتم حفظ مجموعة الأخطاء للكائنات الأبناء في الكائن المستدعي.


=== مساعد التحقُّق confirmation ===
=== المساعد <code>confirmation</code> ===
يجب أن تستخدم هذا التحقُّق عند وجود حقلي نصوص يجب أن يرسلا نفس القيمة تمامًا. مثلًا، قد تحتاج إلى تحقُّق بريد الكتروني أو كلمة مرور. ينشئ هذا التحقُّق حقلًا افتراضيًا ويسمّيه بنفس اسم الحقل الأساسي، يليه النص "confirmation_".
يجب أن تستخدم هذا المساعد عند وجود حقلين نصيَّين يجب على كل منهما أن يرسلا نفس القيمة تمامًا. مثلًا، قد تحتاج إلى تحقُّق بريد الكتروني أو كلمة مرور. ينشئ هذا التحقُّق حقلًا افتراضيًا ويسمّيه بنفس اسم الحقل الأساسي، يليه العبارة "confirmation_".<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :email, confirmation: true
 validates :email, confirmation: true
 
end
end
 
</syntaxhighlight>ويجب أن يبدو نموذج العرض الخاص بك كالتالي:<syntaxhighlight lang="html">
ويجب أن يبدو نموذج العرض الخاص بك كالتالي:
 
<%= text_field :person, :email %>
<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>
<%= text_field :person, :email_confirmation %>


ينفّذ هذا التحقُّق فقط عند كون email_confirmation ليس nil. لطلب التحقُّق بشكل قسري، لا تنسى من إضافة تحقُّق الوجود presence لحقل التحقُّق (سنطلع على تحقُّق الوجود لاحقًا في هذا التوثيق):
</syntaxhighlight>ينفّذ هذا التحقُّق فقط عند كون <code>email_confirmation</code> ليس <code>nil</code>. لطلب التحقُّق بشكل قسري، لا تنسَ إضافة التحقُّق <code>presence</code> للخاصية <code>confirmation</code> (سنطلع على التحقُّق <code>presence</code> لاحقًا في هذا التوثيق):<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :email, confirmation: true
 validates :email, confirmation: true
  validates :email_confirmation, presence: true
 
 validates :email_confirmation, presence: true
 
end
end
 
</syntaxhighlight>يمكنك أيضًا تمرير الخيار <code>case_sensitive:</code> لتحديد فيما إذا كان القيد <code>confirmation</code>  حساسًا لحالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي <code>true</code>.<syntaxhighlight lang="rails">
يمكنك أيضًا تمرير الخيار case_sensitive: لتحديد فيما إذا كان قيد التحقُّق حساسًا لحالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي true.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :email, confirmation: { case_sensitive: false }
 validates :email, confirmation: { case_sensitive: false }
 
end
end
</syntaxhighlight>رسالة الخطأ الافتراضية لهذا المساعد هي "doesn't match confirmation".


رسالة الخطأ الافتراضية لهذا المساعد هي "doesn't match confirmation".
=== المساعد <code>exclusion</code> ===
 
يتأكّد هذا المساعد من أن قيم الحقل المعطى ليست موجودة ضمن المجموعة المحددة. يمكن أن تكون هذه المجموعة أيًّا من الكائنات القابلة للتعداد (enumerable object).<syntaxhighlight lang="rails">
=== مساعد الاقصاء exclusion ===
يتأكّد هذا المساعد من أن قيم الحقل المعطى ليست موجودة ضمن المجموعة المحددة. يمكن أن تكون هذه المجموعة أي نوع مصفوفي.
 
class Account < ApplicationRecord
class Account < ApplicationRecord
 
  validates :subdomain, exclusion: { in: %w(www us ca jp),
 validates :subdomain, exclusion: { in: %w(www us ca jp),
    message: "%{value} is reserved." }
 
   message: "%{value} is reserved." }
 
end
end
 
</syntaxhighlight>يملك المساعد <code>exclusion</code> الخيار <code>in:</code> الذي يقبل مجموعة من القيم التي لن يتم قبولها لخاصيات المتحقق منها. يمكن كتابة <code>in:</code> أيضًا بالشكل <code>within:</code>. يستخدم هذا المثال الخيار <code>message:</code> ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية يمكنك الاطلاع على قسم الرسائل في الأسفل.
يملك مساعد الإقصاء الخيار in: الذي يقبل مجموعة من القيم التي لن يتم قبولها للحقل المؤكد. يمكن كتابة in: أيضًا بالشكل within:. يستخدم هذا المثال الخيار message: ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية يمكنك الاطلاع على توثيق الرسائل.


رسالة الخطأ الافتراضية لهذا المساعد هي "is reserved".
رسالة الخطأ الافتراضية لهذا المساعد هي "is reserved".


=== مساعد التنسيق format ===
=== المساعد <code>format</code> ===
يؤكد هذا المساعد أن قيمة الحقل المعطى توافق التعبير الطبيعي المحدد بالخيار with: كالتالي:
يؤكد هذا المساعد أن قيمة الحقل المعطى توافق التعبير النمطي المحدد بالخيار <code>with:</code> كالتالي:<syntaxhighlight lang="rails">
 
class Product < ApplicationRecord
class Product < ApplicationRecord
 
  validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
<nowiki> validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,</nowiki>
    message: "only allows letters" }
 
   message: "only allows letters" }
 
end
end
 
</syntaxhighlight>بالمثل، يمكنك تحديد أن قيمة الحقل المعطى لا توافق التعبير النمطي المحدد، وذلك بالخيار <code>without:</code>.
بالمثل، يمكنك تحديد أن قيمة الحقل المعطى لا توافق التعبير الطبيعي المحدد، وذلك بالخيار without:.


رسالة الخطأ الافتراضية لهذا المساعد هي "is invalid".
رسالة الخطأ الافتراضية لهذا المساعد هي "is invalid".


=== مساعد التضمين inclusion ===
=== المساعد <code>inclusion</code> ===
يتأكد هذا المساعد من أن قيم الحقل المعطى موجودة ضمن المجموعة المحدة.
يتأكد هذا المساعد من أن قيم الحقل المعطى موجودة ضمن المجموعة المحددة:<syntaxhighlight lang="rails">
 
class Coffee < ApplicationRecord
class Coffee < ApplicationRecord
 
  validates :size, inclusion: { in: %w(small medium large),
 validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }
 
   message: "%{value} is not a valid size" }
 
end
end
 
</syntaxhighlight>يملك المساعد <code>inclusion</code> الخيار <code>in:</code> الذي يستقبل مجموعة من القيم المراد قبولها فقط. يمكن كتابة <code>in:</code> أيضًا بالشكل <code>within:</code>. يستخدم هذا المثال الخيار <code>message:</code> ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية، يمكنك الاطلاع على قسم الرسائل في الأسفل.
يملك مساعد الإقصاء الخيار in: الذي يقبل مجموعة من القيم التي سيتم قبوله فقطا للحقل المؤكد. يمكن كتابة in: أيضًا بالشكل within:. يستخدم هذا المثال الخيار message: ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية يمكنك الاطلاع على توثيق الرسائل.


رسالة الخطأ الافتراضية لهذا المساعد هي "is not included in the list".
رسالة الخطأ الافتراضية لهذا المساعد هي "is not included in the list".


=== مساعد الطول length ===
=== المساعد <code>length</code> ===
يتأكد هذا المساعد من طول قيم الحقل المعطى. يزوّد بمجموعة من الخيارات التي تمكّنك من تحديد قيود الطول بأشكال مختلفة:
يتأكد هذا المساعد من طول قيم الحقل المعطى. يزوّد بمجموعة من الخيارات التي تمكّنك من تحديد قيود الطول بأشكال مختلفة:<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, length: { minimum: 2 }
 validates :name, length: { minimum: 2 }
  validates :bio, length: { maximum: 500 }
 
  validates :password, length: { in: 6..20 }
 validates :bio, length: { maximum: 500 }
  validates :registration_number, length: { is: 6 }
 
 validates :password, length: { in: 6..20 }
 
 validates :registration_number, length: { is: 6 }
 
end
end
 
</syntaxhighlight>الخيارات الممكنة لقيود الطول هي:
الخيارات الممكنة لقيود الطول هي:
* <code>minimum:</code>: لا يمكن للحقل أن يحوي عدد محارف أقل من الطول المعطى.
* minimum: لا يمكن للحقل أن يحوي عدد محارف أقل من الطول المعطى.
* <code>maximum:</code>: لا يمكن للحقل أن يحوي عدد محارف أكثر من الطول المعطى.
* maximum: لا يمكن للحقل أن يحوي عدد محارف أكثر من الطول المعطى.
* <code>in:</code> (أو <code>within:</code>): يمكن للحقل أن يحوي عدد محارف بطول موجود ضمن المجال المعطى.
* in: (أو within:): يمكن للحقل أن يحوي عدد محارف بطول موجود ضمن المجال المعطى.
* <code>is:</code>: يجب على الحقل أن يحوي عدد محارف يساوي الطول المعطى.
* Is: يجب على الحقل أن يحوي عدد محارف يساوي الطول المعطى.
تعتمد رسالة الخطأ الافتراضية على نوع تحقُّق الطول المستخدم. يمكنك تعديل هذه الرسائل عن طريق الخيارات <code>wrong_length:</code>، و <code>too_long:</code>، و <code>too_short:</code>، واستخدام <code>{count}%</code> لاستيفاء طول المحارف المستخدم. يمكنك استخدام الخيار <code>message:</code> لتحديد رسالة خطأ.<syntaxhighlight lang="rails">
تعتمد رسالة الخطأ الافتراضية على نوع تحقُّق الطول المستخدم. يمكنك تعديل هذه الرسائل عن طريق الخيارات wrong_length:، و too_long:، و too_short:، واستخدام {count}% لاستيفاء طول المحارف المستخدم. يمكنك استخدام الخيار message: لتحديد رسالة خطأ.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :bio, length: { maximum: 1000,
 validates :bio, length: { maximum: 1000,
    too_long: "%{count} characters is the maximum allowed" }
 
   too_long: "%{count} characters is the maximum allowed" }
 
end
end
</syntaxhighlight>تجب الملاحظة أن رسائل الخطأ الافتراضية هي في صيغة الجمع مثل:<syntaxhighlight lang="text">
"is too short (minimum is %{count} characters)"
</syntaxhighlight>لهذا السبب، عند كون <code>minimum:</code> تساوي 1، يجب تزويد رسالة معدلة أو استخدام الخيار <code>presence: true</code> بدلًا من ذلك. عندما يملك الخيار <code>in:</code> أو <code>within:</code> حدًا أصغريًا مساويًا للواحد، يجب عليك تزويد رسالة معدلة أو استخدام الخيار <code>presence</code> قبل <code>length</code>.


تجب الملاحظة أن رسائل الخطأ الافتراضية هي في صيغة الجمع. (مثال "is too short (minimum is %{count} characters)"). لهذا السبب، عند كون minimum: تساوي 1 يجب تزويد رسالة معدلة أو استخدام الخيار presence: true بدلًا من ذلك. عندما يملك الخيار in: أو within: حدًا أصغريًا مساويًا للواحد، يجب عليك تزويد رسالة معدلة أو استخدام الخيار presence قبل الطول.
=== المساعد <code>numericality</code> ===
 
يتأكد هذا المساعد من أن الحقل يحتوي فقط على قيم رقمية. افتراضيًا، يطابق هذا المساعد إشارة الرقم الاختيارية، متبوعةً بعدد صحيح أو عشري. لتحديد قبول الأعداد الصحيحة فقط، يمكن تعيين الخيار <code>only_integer:</code> إلى <code>true</code>.
=== مساعد الرقمية numericality ===
يتأكد هذا المساعد من أن الحقل يحتوي فقط على قيم رقمية. افتراضيًا، يطابق هذا المساعد إشارة الرقم الاختيارية، ملحوقة برقم عشري صحيح أو طبيعي ذو فاصلة عائمة. لتحديد قبول فقط الأعداد الصحيحة يمكن تعيين الخيار only_integer: إلى true.
 
وفي حال استخدمت only_integer:، سيتم استخدام التعبير الطبيعي التالي:


وفي حال ضبط <code>only_integer:</code> إلى القيمة <code>true</code>، فسيتم استخدام التعبير النمطي التالي:<syntaxhighlight lang="text">
/\A[+-]?\d+\z/
/\A[+-]?\d+\z/
 
</syntaxhighlight>وذلك للتحقُّق من قيمة الخاصية. فيما عدا ذلك، سيحاول تحويل قيمة الحقل إلى عدد باستخدام <code>[[Ruby/Float|Float]]</code>.<syntaxhighlight lang="rails">
وذلك لتحقُّق قيمة الحقل. فيما عدا ذلك، سيحاول تحويل قيمة الحقل إلى رقم باستخدام Float.
 
class Player < ApplicationRecord
class Player < ApplicationRecord
 
  validates :points, numericality: true
 validates :points, numericality: true
  validates :games_played, numericality: { only_integer: true }
 
 validates :games_played, numericality: { only_integer: true }
 
end
end
 
</syntaxhighlight>إلى جانب <code>only_integer:</code>، يقبل هذا المساعد مجموعة من الخيارات لإضافة قيود على القيم المقبولة:
إلى جانب only_integer:، يقبل هذا المساعد مجموعة من الخيارات لإضافة القيود على القيم المستقبلة:
* الخيار <code>greater_than:</code>: يحدد أن القيمة يجب أن تكون أكبر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than %{count}".
* الخيار greater_than: يحدد أن القيمة يجب أن تكون أكبر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than %{count}".
* الخيار <code>greater_than_or_equal_to:</code>: يحدد أن القيمة يجب أن تكون أكبر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than or equal to %{count}".
* الخيار greater_than_or_equal_to: يحدد أن القيمة يجب أن تكون أكبر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than or equal to %{count}".
* الخيار <code>equal_to:</code>: يحدد أن القيمة يجب أن تكون تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be equal to %{count}"
* الخيار equal_to: يحدد أن القيمة يجب أن تكون تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be equal to %{count}"
* الخيار <code>less_than:</code>: يحدد أن القيمة يجب أن تكون أصغر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than %{count}".
* الخيار less_than: يحدد أن القيمة يجب أن تكون أصغر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than %{count}".
* الخيار <code>less_than_or_equal_to:</code>: يحدد أن القيمة يجب أن تكون أصغر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than or equal to %{count}".
* الخيار less_than_or_equal_to: يحدد أن القيمة يجب أن تكون أصغر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than or equal to %{count}".
* الخيار <code>other_than:</code>: يحدد أن القيمة يجب أن تكون غير مساوية للقيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be other than %{count}"
* الخيار other_than: يحدد أن القيمة يجب أن تكون لا تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be other than %{count}"
* الخيار <code>odd:</code>: يحدد أن القيمة يجب أن تكون عددًا فرديًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be odd".
* الخيار odd: يحدد أن القيمة يجب أن تكون عدد فردي. رسالة الخطأ الافتراضية لهذا الخيار هي "must be odd".
* الخيار <code>even:</code>: يحدد أن القيمة يجب أن تكون عددًا زوجيًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be even".
* الخيار even: يحدد أن القيمة يجب أن تكون عدد زوجي. رسالة الخطأ الافتراضية لهذا الخيار هي "must be even".
'''ملاحظة''': افتراضيًا، لا يسمح المساعد <code>numericality</code> بالقيم nil. يمكنك استخدام الخيار <code>allow_nil:</code> للسماح بها.
ملاحظة: افتراضيًا، لا يسمح مساعد الرقمية numericality بالقيم nil. يمكنك استخدام الخيار allow_nil: للسماع بها.


الرسالة الافتراضية لهذا المساعد هي "is not a number".
الرسالة الافتراضية لهذا المساعد هي "is not a number".


=== مساعد الوجود presence ===
=== المساعد <code>presence</code> ===
يؤكد هذا المساعد وجود قيم الحقل المحدد، أي عدم استقبال قيم فارغة. يستخدم هذا المساعد التابع ?blank للتأكد من أن القيمة هي nil، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.
يتحقق هذا المساعد من أن عدم خلو خاصيات محدَّدة من أية قيمة. يستخدم هذا المساعد التابع <code>?blank</code> للتأكد من أن القيمة هي <code>nil</code>، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, :login, :email, presence: true
 validates :name, :login, :email, presence: true
 
end
end
 
</syntaxhighlight>إذا أردت أن تكون متأكدًا من وجود ارتباط ما، يجب أن تتحقّق من وجود الكائن المرتبط بنفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.<syntaxhighlight lang="rails">
إذا أردت أن تكون متأكدًا من وجود ارتباط ما، يجب أن تتحقّق من وجود الكائن المرتبط بنفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.
 
class LineItem < ApplicationRecord
class LineItem < ApplicationRecord
 
  belongs_to :order
 belongs_to :order
  validates :order, presence: true
 
 validates :order, presence: true
 
end
end
 
</syntaxhighlight>من أجل تحقُّق السجلات المرتبطة الضروري وجودها، يجب تحديد الخيار <code>inverse_of:</code> من أجل الارتباط:<syntaxhighlight lang="rails">
من أجل تحقُّق السجلات المرتبطة الضروري وجودها، يجب تحديد الأمر inverse_of: على الارتباط:
 
class Order < ApplicationRecord
class Order < ApplicationRecord
 
  has_many :line_items, inverse_of: :order
 has_many :line_items, inverse_of: :order
 
end
end
</syntaxhighlight>في حال قمت بالتحقُّق من وجود كائن مرتبط عن طريق العلاقة has_one أو has_many، سيتفقّد من كون الكائن  غير محقق للتابع <code>?blank</code> ولا التابع <code>?marked_for_destruction</code>.


في حال قمت بتحقُّق وجود كائن مرتبط عن طريق علاقة has_one أو has_many، سيتفقّد من الكائن عن طريق التوابع ?blank أو ?marked_for_destruction.
لمَّا كان <code>?false.blank</code> يعيد القيمة <code>true</code>، فإذا أردت التحقُّق من وجود حقل من النوع <code>Boolean</code>، فيجب عليك القيام بأحد التحققين التاليين:<syntaxhighlight lang="rails">
 
validates :boolean_field_name, inclusion: { in: [true, false] }
بما أنّ ?false.blank تعيد القيمة true، إذا أردت تحقُّق وجود حقل من النوع البولياني، يجب عليك القيام بأحد التحقُّقات التالية:
validates :boolean_field_name, exclusion: { in: [nil] }
 
</syntaxhighlight>باستخدام أحد هذين التحقُّقين، يمكنك التأكد من عدم استقبال الحقل المنطقي القيمة <code>nil</code>.
<nowiki>validates :boolean_field_name, inclusion: { in: [true, false] }</nowiki>
 
<nowiki>validates :boolean_field_name, exclusion: { in: [nil] }</nowiki>
 
باستخدام أحد هذه التحقُّقات، يمكنك التأكد من عدم استقبال الحقل البولياني القيم nil.
 
=== مساعد الغياب absence ===
يؤكد هذا المساعد غياب وعدم وجود قيم الحقل المحدد. يستخدم هذا المساعد التابع ?present للتأكد من أن القيمة هي ليست nil، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.


=== المساعد <code>absence</code> ===
يؤكد هذا المساعد غياب وعدم وجود قيم خاصية محددة. يستخدم هذا المساعد التابع <code>?present</code> للتأكد من أن القيمة هي ليست <code>nil</code>، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.<syntaxhighlight lang="rails">
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, :login, :email, absence: true
 validates :name, :login, :email, absence: true
 
end
end
 
</syntaxhighlight>إذا أردت أن تكون متأكدًا من غياب ارتباط ما، يجب أن تتحقّق من غياب الكائن المرتبط نفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.<syntaxhighlight lang="rails">
إذا أردت أن تكون متأكدًا من غياب ارتباط ما، يجب أن تتحقّق من غياب الكائن المرتبط بنفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.
 
class LineItem < ApplicationRecord
class LineItem < ApplicationRecord
 
  belongs_to :order
 belongs_to :order
  validates :order, absence: true
 
 validates :order, absence: true
 
end
end
 
</syntaxhighlight>من أجل تحقُّق السجلات المرتبطة الضروري غيابها، يجب تحديد الخيار <code>inverse_of:</code> من أجل الارتباط:<syntaxhighlight lang="rails">
من أجل تحقُّق السجلات المرتبطة الضروري غيابها، يجب تحديد الأمر inverse_of: على الارتباط:
 
class Order < ApplicationRecord
class Order < ApplicationRecord
 
  has_many :line_items, inverse_of: :order
 has_many :line_items, inverse_of: :order
 
end
end
</syntaxhighlight>في حال قمت بالتحقُّق من غياب كائن مرتبط عن طريق علاقة has_one أو has_many، سيتفقّد من أنَّ الكائن غير محقق للتابع <code>?present</code> ولا التابع <code>?marked_for_destruction</code>.


في حال قمت بتحقُّق غياب كائن مرتبط عن طريق علاقة has_one أو has_many، سيتفقّد من الكائن عن طريق التوابع ?present أو ?marked_for_destruction.
لما كان <code>?false.present</code> يعيد القيمة <code>false</code>، فإذا أردت التحقُّق من غياب حقل من النوع المنطقي، فيجب عليك استعمال:<syntaxhighlight lang="rails">
 
validates :field_name, exclusion: { in: [true, false] }
بما أنّ ?false.present تعيد القيمة falseإذا أردت تحقُّق غياب حقل من النوع البولياني، يجب عليك القيام بأحد التحقُّقات التالية:
</syntaxhighlight>رسالة الخطأ الافتراضية لهذا المساعد هي "must be blank".
 
<nowiki>validates :field_name, exclusion: { in: [true, false] }</nowiki>
 
رسالة الخطأ الافتراضية لهذا المساعد هي "must be blank".
 
مساعد التميز uniqueness
 
يؤكد هذا المساعد أن قيمة الحقل فريدة وغير مكررة قبل حفظ الكائن. لا ينشئ هذا المساعد قيد التميز في قاعدة البيانات، لذلك قد تحدث حالة إضافة سجلّين بقيمتين متساويتين في حقل واحد تريده أن يكون فريدًا، وذلك من قبل اتصالين منفصلين لقاعدة البيانات. لتفادي هذا، يجب عليك إنشاء فهرس تميز على هذا الحقل في قاعدة البيانات.


=== المساعد <code>uniqueness</code> ===
يتحقق هذا المساعد من أنَّ قيمة الحقل فريدة وغير مكررة قبل حفظ الكائن. لا ينشئ هذا المساعد قيد التميز في قاعدة البيانات، لذلك قد تحدث حالة إضافة سجلّين بقيمتين متساويتين في حقل واحد تريده أن يكون فريدًا، وذلك من قبل اتصالين منفصلين مع قاعدة البيانات. لتفادي هذا، يجب عليك إنشاء فهرس فريد  لهذا الحقل في قاعدة البيانات.<syntaxhighlight lang="rails">
class Account < ApplicationRecord
class Account < ApplicationRecord
 
  validates :email, uniqueness: true
 validates :email, uniqueness: true
 
end
end
</syntaxhighlight>يجري تنفيذ التحقُّق عن طريق تنفيذ استعلام [[SQL]] على جدول النموذج، بحثًا عن سجل موجود يملك ذات القيمة في الحقل المعطى.


يحدث التحقُّق عن طريق تنفيذ استعلام SQL على جدول النموذج، بحثًا عن سجل موجود يملك ذات القيمة في الحقل المعطى.
يمكن استخدام الخيار <code>scope:</code> لتحديد خاصية أو أكثر مستخدمة للتحقق <code>uniqueness</code>:<syntaxhighlight lang="rails">
 
يمكن استخدام الخيار scope: لتحديد حقل أو أكثر مستخدم لتقييد التميز:
 
class Holiday < ApplicationRecord
class Holiday < ApplicationRecord
 
  validates :name, uniqueness: { scope: :year,
 validates :name, uniqueness: { scope: :year,
    message: "should happen once per year" }
 
   message: "should happen once per year" }
 
end
end
</syntaxhighlight>إذا أردت إنشاء قيدٍ على قاعدة البيانات لتفادي تجاوز التحقُّقات <code>uniqueness</code> المستخدمة للخيار <code>scope:</code>، يجب أن تنشئ فهرسًا فريدًا في كلا الحقلين في قاعدة البيانات. اقرأ [http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html دليل MySQL] للمزيد من المعلومات حول إنشاء فهرس لحقول متعددة أو [https://www.postgresql.org/docs/current/static/ddl-constraints.html دليل PostgreSQL] لأمثلة حول قيود التميز المستخدمة على نطاق مجموعة من الحقول في آن معًا.


إذا أردت إنشاء قيد تميز على قاعدة البيانات لتفادي تجاوز تحقُّقات التميز المستخدمة للخيار scope:، يجب أن تنشئ فهرس تميز على كلا الحقلين في قاعدة البيانات. اقرأ دليل MySQL للمزيد من المعلومات حول إنشاء فهرس لحقول متعددة أو دليل PostgreSQL لأمثلة حول قيود التميز المستخدمة على نطاق مجموعة من الحقول في آن معًا.
هناك أيضًا الخيار <code>case_sensitive:</code> لتحديد فيما إذا كان القيد <code>uniqueness</code> يراعي حالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي <code>true</code>.<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
هناك أيضًا خيار case_sensitive: لتحديد فيما إذا كان قيد التميز يراعي حالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي true.class Person < ApplicationRecord
  validates :name, uniqueness: { case_sensitive: false }
 
 validates :name, uniqueness: { case_sensitive: false }
 
end
end
 
</syntaxhighlight>'''ملاحظة''': بعض قواعد البيانات مهيّئة لتنفيذ عمليات البحث غير الحساسة لحالة الأحرف في كلتا الحالتين.
ملاحظة: بعض قواعد المعطيات مهيّئة لتنفيذ عمليات البحث غير الحساسة لحالة الأحرف في كلتا الحالتين.


رسالة الخطأ الافتراضية لهذا المساعد هي "has already been taken".
رسالة الخطأ الافتراضية لهذا المساعد هي "has already been taken".


مساعد التحقُّق باستخدام validates_with
=== المساعد <code>validates_with</code> ===
 
يمرر هذا المساعد السجل الواجب التحقق منه إلى صنف آخر منفصل للقيام بعملية التحقُّق.<syntaxhighlight lang="rails">
يمرر هذا المساعد السجل الواجب تحقُّقه إلى صنف مؤكد آخر للقيام بعملية التحقُّق.
 
class GoodnessValidator < ActiveModel::Validator
class GoodnessValidator < ActiveModel::Validator
 
  def validate(record)
 def validate(record)
    if record.first_name == "Evil"
 
      record.errors[:base] << "This person is evil"
   if record.first_name == "Evil"
    end
 
  end
     record.errors[:base] << "This person is evil"
 
   end
 
 end
 
end
end
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates_with GoodnessValidator
 validates_with GoodnessValidator
 
end
end
</syntaxhighlight>'''ملاحظة''': الأخطاء المضافة إلى <code>[record.errors[:base</code> تتعلق بحالة السجل ككل، وليس بحقل معين.


ملاحظة: الأخطاء المضافة لـ [record.errors[:base تتعلق بحالة السجل ككل، وليس بحقل معين.
يقبل المساعد <code>validates_with</code> صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق الصنف <code>validator</code>.
 
يقبل المساعد validates_with صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق أصناف التحقُّق.


لكتابة تابع التحقُّق، يجب أن تستقبل وسيطًا يمثل السجل المطلوب تحقُّقه.
لكتابة تابع التحقُّق، يجب أن تستقبل وسيطًا يمثل السجل المطلوب تحقُّقه.


كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات if: و unless: و on:. إذا مرّرت خيارات إضافية، سيتم تمريرها إلى صنف التحقُّق ضمن الوسيط options:
كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات <code>if:</code> و <code>unless:</code> و <code>on:</code>. إذا مرّرت خيارات إضافية، فسيتم تمريرها إلى الصنف <code>validator</code> ضمن الوسيط <code>options</code>:<syntaxhighlight lang="rails">
 
class GoodnessValidator < ActiveModel::Validator
class GoodnessValidator < ActiveModel::Validator
 
  def validate(record)
 def validate(record)
    if options[:fields].any?{|field| record.send(field) == "Evil" }
 
      record.errors[:base] << "This person is evil"
   if options[:fields].any?{|field| record.send(field) == "Evil" }
    end
 
  end
     record.errors[:base] << "This person is evil"
 
   end
 
 end
 
end
end
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates_with GoodnessValidator, fields: [:first_name, :last_name]
 validates_with GoodnessValidator, fields: [:first_name, :last_name]
 
end
end
</syntaxhighlight>من الجدير بالملاحظة أنّه يتم تهيئة الصنف <code>validator</code> '''مرّة واحدة''' فقط ضمن دورة حياة التطبيق ككل، وليس في كل مرة يتم تنفيذ تحقُّق معيّن؛ لذلك كن حذرًا أثناء التعامل مع متغيرات النسخة داخله.


من الجدير بالملاحظة أنّه يتم تهيئة صنف التحقُّق مرّة واحدة فقط ضمن دورة حياة التطبيق ككل، وليس في كل مرة يتم تنفيذ تحقُّق معيّن؛ لذلك كن حذرًا أثناء التعامل مع المتحولات داخل أصناف التحقُّق.
إذا كان صنف التحقُّق <code>validator</code> الخاص بك معقدًا لدرجة حاجته لمتغيّرات نسخة، فيمكنك استخدام كائنات [[Ruby|روبي]] البسيطة بدلًا من ذلك:<syntaxhighlight lang="rails">
 
إذا كان صنف التحقُّق الخاص بك معقدًا لدرجة حاجته لمتغيّرات المثل، يمكنك استخدام كائنات Ruby البسيطة بدلًا من ذلك:
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validate do |person|
 validate do |person|
    GoodnessValidator.new(person).validate
 
  end
   GoodnessValidator.new(person).validate
 
 end
 
end
end
 
class GoodnessValidator
class GoodnessValidator
 
  def initialize(person)
 def initialize(person)
    @person = person
 
  end
   @person = person
 
  def validate
 end
    if some_complex_condition_involving_ivars_and_private_methods?
 
      @person.errors[:base] << "This person is evil"
 def validate
    end
 
  end
   if some_complex_condition_involving_ivars_and_private_methods?
 
  # ...
     @person.errors[:base] << "This person is evil"
 
   end
 
 end
 
 # ...
 
end
end
</syntaxhighlight>


مساعد تحقُّق الكل validates_each
=== المساعد <code>validates_each</code> ===
 
يتحقَّق هذا المساعد من مجموعة من الخاصيات مقابل كتلة من التعليمات البرمجية، إذ لا يملك تابع تحقُّق معرف مسبقًا. يجب عليك إنشاء هذا التابع باستخدام كتلة، وكل خاصية ممرّرة للمساعد <code>validates_each</code> سيتم اختبارها في هذه الكتلة. في المثال التالي، لا نريد من الأسماء الأولى والكنيات أن تبدأ بأحرف صغيرة.<syntaxhighlight lang="rails">
يؤكد هذا المساعد مجموعة من الحقول على هيكل من التعليمات، إذ لا يملك تابعًا مخصّصًا للتحقُّق. يجب عليك إنشاء هذا التابع باستخدام هيكل، وكل حقل ممرّر للمساعد validates_each سيتم اختباره في هذا الهيكل. في المثال التالي، لا نريد من الأسماء الأولى والكنيات أن تبدأ بأحرف صغيرة.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates_each :name, :surname do |record, attr, value|
 validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
 
  end
   record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
 
 end
 
end
end
</syntaxhighlight>تستقبل الكتلة السجل، واسم الخاصية وقيمتها. يمكنك القيام بأي عملية داخل الكتلة للتأكد من صحة البيانات المستقبلة. في حال فشل التحقُّق، يجب عليك إضافة رسالة خطأ إلى النموذج، موضّحًا سبب فشل عملية التحقق من صحّة البيانات.


يستقبل الهيكل السجل، واسم الحقل وقيمته. يمكنك القيام بأي عملية داخل الهيكل للتأكد من صحة البيانات المستقبلة. في حال فشل التحقُّق، يجب عليك إضافة رسالة خطأ إلى النموذج، موضّحًا سبب عدم صحّة البيانات.
== خيارات عملية التحقق الشائعة ==
 
إليك مجموعة من خيارات التحقُّق الشائعة المستخدمة في [[Rails/active record|Active Record]]:
خيارات التحقُّق الشائعة
 
إليك مجموعة من خيارات التحقُّق الشائعة المستخدمة في السجل الفعال:
 
الخيار allow_nil:
 
يتجاوز الخيار allow_nil: التحقُّق عندما تكون القيمة الواجب تحقُّقها nil.


=== الخيار <code>allow_nil:</code> ===
يتجاوز الخيار <code>allow_nil:</code> التحقُّق عندما تكون القيمة الواجب التحقق منها هي <code>nil</code>.<syntaxhighlight lang="rails">
class Coffee < ApplicationRecord
class Coffee < ApplicationRecord
 
  validates :size, inclusion: { in: %w(small medium large),
 validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }, allow_nil: true
 
   message: "%{value} is not a valid size" }, allow_nil: true
 
end
end
</syntaxhighlight>لقراءة كل الخيارات حول الوسيط <code>message</code>، اطّلع على [https://guides.rubyonrails.org/active_record_validations.html#message التوثيق الخاص به].


لقراءة كل الخيارات حول وسيط الرسالة اطّلع على خيار الرسالة.
=== الخيار <code>allow_blank:</code> ===
 
يشابه هذا الخيار الخيار <code>allow_nil:</code>، إذ يتجاوز التحقُّق في حال كانت قيمة الخاصية هي <code>?blank</code>، مثل القيمة <code>nil</code> أو سلسلة نصية فارغة.<syntaxhighlight lang="rails">
الخيار allow_blank:
 
يشابه هذا الخيار الخيار allow_nil:، إذ يتجاوز التحقُّق في حال كانت قيمة الحقل ?blank، كالقيمة nil أو سلسلة محرفية فارغة.
 
class Topic < ApplicationRecord
class Topic < ApplicationRecord
 
  validates :title, length: { is: 5 }, allow_blank: true
 validates :title, length: { is: 5 }, allow_blank: true
 
end
end
 
Topic.create(title: "").valid?  # => true
Topic.create(title: "").valid? # => true
 
Topic.create(title: nil).valid? # => true
Topic.create(title: nil).valid? # => true
</syntaxhighlight>


خيار الرسالة message:
=== الخيار <code>message:</code> ===
كما رأيت مسبقًا، يمكّنك الخيار <code>message:</code> من تحديد رسالة خطأ يراد إضافتها لمجموعة الأخطاء <code>errors</code> عند فشل عملية التحقُّق. عند عدم تحديد هذا الخيار، يرسل [[Rails/active record|Active Record]] رسالة الخطأ الافتراضية من أجل كل مساعد تحقُّق. يقبل الخيار <code>message:</code> إما [[Ruby/String|سلسلة نصية]] أو كائنًا من النوع <code>[[Ruby/Proc|Proc]]</code>.


كما اطّلعت مسبقًا، يمكّنك الخيار message: من تحديد رسالة خطأ ليتم إضافتها لمجموعة الأخطاء errors عند فشل التحقُّق. عند عدم تحديد هذا الخيار، يقوم السجل الفعال بإرسال رسالة الخطأ الافتراضية من أجل كل مساعد تحقُّق. يقبل الخيار message: إما String أو Proc.
يمكن للسلسلة النصية الممررة للخيار <code>message:</code> أن تحوي كل من <code>{value}%</code> أو <code>{attribute}%</code> أو <code>{model}%</code> التي يتم استبدالها دينياميكيًا عند فشل التحقُّق. يتم تنفيذ هذا الاستبدال باستخدام الجوهرة [[Rails/i18n|I18n]].
 
يمكن للقيمة String الممررة للخيار message: أن تحوي كل من {value}% أو {attribute}% أو {model}%، التي يتم استبدالها دينياميكيًا عند فشل التحقُّق. يتم تنفيذ هذا الاستبدال باستخدام الجوهرة I18n.
 
للقيمة Proc الممرة للخيار message:، يتم تمرير وسيطين: الكائن المؤكد، ومجموعة بأزواج المفاتيح والقيم لـ model: و attribute: و value:.


أمَّا في حال استعمال الكائن <code>[[Ruby/Proc|Proc]]</code> مع الخيار <code>message:</code>، يتم تمرير وسيطين هما: الكائن المراد التحقق منه، ومجموعة بأزواج المفاتيح والقيم التالية: <code>model:</code> و <code>attribute:</code> و <code>value:</code>.<syntaxhighlight lang="rails">
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  # رسالة ثابتة
 # رسالة ثابتة
  validates :name, presence: { message: "must be given please" }
 
 validates :name, presence: { message: "must be given please" }
 # %{value} رسالة مع قيمة ديناميكية. يتم استبدال
 
 # متاحتان أيضًا %{model} و %{attribute} بالقيمة الحقيقية للحقل. إن
 # رسالة مع قيمة ديناميكية. %{value} يتم استبدالها
  validates :age, numericality: { message: "%{value} seems wrong" }
 
 # بالقيمة الحقيقية للحقل. %{attribute} و %{model} أيضًا متاحة.
  # Proc
 
  validates :username,
 validates :age, numericality: { message: "%{value} seems wrong" }
    uniqueness: {
 
      # object = الكائن المراد التحقق منه
 # Proc
      # data = { model: "Person", attribute: "Username", value: <username> }
 
      message: ->(object, data) do
 validates :username,
        "Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}"
 
      end
   uniqueness: {
    }
 
     # object = الكائن المؤكد
 
     # data = { model: "Person", attribute: "Username", value: <username> }
 
     message: ->(object, data) do
 
       "Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}"
 
     end
 
   }
 
end
end
</syntaxhighlight>


الخيار on:
=== الخيار <code>on:</code> ===
 
يمكّنك هذا الخيار من تحديد مكان التحقُّق. السلوك الافتراضي لجميع مساعدات التحقُّق المضمنة هو تشغيل التحقُّق عند الحفظ (أي عند إنشاء أو تحديث السجلات). إذا أردت تغيير هذا السلوك، فيمكنك تحديد الخيار <code>on:</code> على <code>create:</code> لتشغيل التحقُّق فقط عند إنشاء كائن جديد، أو على <code>update:</code> لتشغيل التحقُّق فقط عند تعديل كائن موجود مسبقًا.<syntaxhighlight lang="rails">
يمكّنك هذا الخيار من تحديد مكان التحقُّق. السلوك الافتراضي لجميع مساعدات التحقُّق المضمنة هو تشغيل التحقُّق عند الحفظ (أي عند إنشاء أو تعديل سجلات). إذا أردت تغييرها، يمكنك تحديد الخيار on: على create: لتشغيل التحقُّق فقط عند إنشاء كائن جديد، أو على update: لتشغيل التحقُّق فقط عند تعديل كائن موجود مسبقًأ.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  # سيكون بالإمكان تحديث  عنوان البريد الإلكتروني مع قيمة مكررة
 # it will be possible to update email with a duplicated value
  validates :email, uniqueness: true, on: :create
 
 validates :email, uniqueness: true, on: :create
  # سيكون بالإمكان إنشاء سجل  مع قيمة غير عددية للعمر
 
  validates :age, numericality: true, on: :update
 # it will be possible to create the record with a non-numerical age
 
  # القيمة الافتراضية هي التحقق عند الإنشاء والتحديث
 validates :age, numericality: true, on: :update
  validates :name, presence: true
 
 # the default (validates on both create and update)
 
 validates :name, presence: true
 
end
end
 
</syntaxhighlight>يمكنك أيضًا استخدام الخيار <code>on:</code> لتعريف سياق مخصّص. يجب تشغيل السياق المخصص بشكل ظاهري عن طريق تمرير اسم السياق إلى التوابع <code>?valid</code> أو <code>?invalid</code> أو <code>save</code>.<syntaxhighlight lang="rails">
يمكنك أيضًا استخدام الخيار on: لتعريف سياق مخصّص. يجب تشغيل السياق المخصص بشكل ظاهري عن طريق تمرير اسم السياق إلى التوابع ?valid أو ?invalid أو save.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :email, uniqueness: true, on: :account_setup
 validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
 
 validates :age, numericality: true, on: :account_setup
 
end
end
 
person = Person.new
person = Person.new
</syntaxhighlight>يتم تنفيذ <code>(person.valid?(:account_setup</code> عند التحقُّقين دون حفظ السجل. ويقوم التابع <code>(person.sav(context: :account_setup</code> بالتحقُّق من الكائن <code>person</code> في السياق <code>account_setup</code> قبل الحفظ. عند التشغيل بشكل ظاهري، يتم التحقُّق من السجلات عن طريق عمليات التحقق المحددة فقط لسجلات هذا السياق أو التي لا تملك سياقًا مطلقًا.


يتم تنفيذ (person.valid?(:account_setup عند التحقُّقين دون حفظ السجل. ويقوم التابع (person.sav(context: :account_setup بتحقُّق الكائن person في سياق account_setup قبل الحفظ. عند التشغيل بشكل ظاهري، يتم تحقُّق السجلات عن طريق المؤكدات المحددة فقط للسجلات لهذا السياق أو التي لا تملك سياقًا مطلقًا.
== التحقُّقات الصارمة ==
 
يمكنك تحديد فيما إذا كان تحقُّق ما صارم (strict) أم لا ورفع الاستثناء <code>ActiveModel::StrictValidationFailed</code> عند فشل عملية التحقق.<syntaxhighlight lang="rails">
التحقُّقات الدقيقة
 
يمكنك تحديد فيما إذا كان تحقُّق ما دقيق أم لا ورفع استثناء ActiveModel::StrictValidationFailed عند عدم صحة الكائن.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, presence: { strict: true }
 validates :name, presence: { strict: true }
 
end
end
 
Person.new.valid?  # => ActiveModel::StrictValidationFailed: Name can't be blank
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
 
</syntaxhighlight>يمكنك أيضًا تمرير استثناءات مخصّصة للخيار <code>strict:</code>.<syntaxhighlight lang="rails">
يمكنك أيضًا تمرير استثناءات مخصّصة للخيار strict:.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
 validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
 
end
end
Person.new.valid?  # => TokenGenerationException: Token can't be blank
</syntaxhighlight>


Person.new.valid?  # => TokenGenerationException: Token can't be blank
== التحققات الشرطية ==
 
في بعض الأحيان، قد يكون من المنطقي التحقُّق من سجل ما فقط عند تحقق شرط معيّن. يمكنك إنجاز ذلك عن طريق الخيارين <code>if:</code> و <code>unless:</code>، اللذَين يستقبلان [[Ruby/Symbol|رمزًا]]، أو كائنًا من النوع <code>[[Ruby/Proc|Proc]]</code>، أو [[Ruby/Array|مصفوفة]]. يمكنك استخدام الخيار <code>if:</code> لتحديد متى يجب تنفيذ عملية التحقُّق. وفي حال أردت تحديد متى لا يجب تنفيذ التحقُّق، يمكنك استخدام الخيار <code>unless:</code>.
التحقُّقات الشرطية
 
في بعض الأحيان، قد يكون من المنطقي تحقُّق سجل ما فقط عند تحقق شرط معيّن. يمكنك تحقيق ذلك عن طريق الخيارات if: و unless:، التي تستقبل رمزًا، Proc، أو مصفوفة Array. يمكنك استخدام الخيار if: لتحديد متى يجب تنفيذ التحقُّق. وفي حال أردت تحديد متى لا يجب تنفيذ التحقُّق، يمكنك استخدام الخيار unless:.
 
استخدام رمز مع الخيارات if: و unless:
 
يمكنك ربط الخيارات if: و unless: مع رمز موافق لاسم التابع المنادى قبل عملية التحقُّق. هذا الخيار هو الخيار الأكثر استخدامًا.


=== استخدام رمز مع الخيارين <code>if:</code> و <code>unless:</code> ===
يمكنك ربط الخيارين <code>if:</code> و <code>unless:</code> مع رمز موافق لاسم التابع المستدعى قبل عملية التحقُّق. هذا الخيار هو الخيار الأكثر استخدامًا.<syntaxhighlight lang="rails">
class Order < ApplicationRecord
class Order < ApplicationRecord
 
  validates :card_number, presence: true, if: :paid_with_card?
 validates :card_number, presence: true, if: :paid_with_card?
 
  def paid_with_card?
 def paid_with_card?
    payment_type == "card"
 
  end
   payment_type == "card"
 
 end
 
end
end
</syntaxhighlight>


استخدام Proc مع الخيارات if: و unless:
=== استخدام الكائن <code>Proc</code> مع الخيارين <code>if:</code> و <code>unless:</code> ===
 
أخيرًا، يمكنك ربط الخيارين <code>if:</code> و <code>unless:</code> مع كائن من النوع <code>[[Ruby/Proc|Proc]]</code> يتم مناداته. باستخدام الكائن <code>[[Ruby/Proc|Proc]]</code>، يمكنك التحقُّق شرطيًا ضمن سطر من التعليمات بدلًأ من تابع منفصل. هذا الخيار هو مفضّل لمحبّي الأسطر الواحدة.<syntaxhighlight lang="rails">
نهايةً، يمكنك ربط الخيارات if: و unless: مع كائن Proc يتم مناداته. باستخدام الكائن Proc، يمكنك التحقُّق شرطيًا ضمن سطر من التعليمات بدلًأ من تابع منفصل. هذا الخيار هو مفضّل لمحبّي الأسطر الواحدة.
 
class Account < ApplicationRecord
class Account < ApplicationRecord
 
  validates :password, confirmation: true,
 validates :password, confirmation: true,
    unless: Proc.new { |a| a.password.blank? }
 
   unless: Proc.new { |a| a.password.blank? }
 
end
end
</syntaxhighlight>


تجميع التحقُّقات الشرطية
=== تجميع التحقُّقات الشرطية ===
 
قد يكون من المفيد أحيانًا أن تستخدم مجموعة من التحقُّقات شرطًا واحدًا. يمكن تنفيذ ذلك عن باستخدام <code>with_options</code>.<syntaxhighlight lang="rails">
قد يكون من المفيد أحيانًا أن تستخدم مجموعة من التحقُّقات شرطًا واحدًا. يمكن تنفيذ ذلك عن باستخدام with_options.
 
class User < ApplicationRecord
class User < ApplicationRecord
 
  with_options if: :is_admin? do |admin|
 with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
 
    admin.validates :email, presence: true
   admin.validates :password, length: { minimum: 10 }
  end
 
   admin.validates :email, presence: true
 
 end
 
end
end
</syntaxhighlight>جميع التحقُّقات الواقعة داخل الكتلة التي تخص <code>with_options</code> سيمرّر لها الشرط <code>if: :is_admin?‎</code> تلقائيًّا.


جميع التحقُّقات الواقعة داخل هيكل with_options يمرّر لها الشرط ?if: is_admin.
=== دمج شروط التحقُّق ===
 
وفي الطرف الآخر، عندما تحدد مجموعة من الشروط متى يجب تنفيذ تحقُّق معيّن، يمكن استخدام مصفوفة. أي يمكنك استخدام الخيارين <code>if:</code> و <code>unless:</code> على التحقُّق نفسه.<syntaxhighlight lang="rails">
تجميع شروط التحقُّق
 
وفي الطرف الآخر، عندما تحدد مجموعة من الشروط متى يجب تنفيذ تحقُّق معيّن، يمكن استخدام مصفوفة. أي يمكنك استخدام الخيارين if: و unless: على التحقُّق نفسه.
 
class Computer < ApplicationRecord
class Computer < ApplicationRecord
 
  validates :mouse, presence: true,
 validates :mouse, presence: true,
                    if: [Proc.new { |c| c.market.retail? }, :desktop?],
 
                    unless: Proc.new { |c| c.trackpad.present? }
                   if: [Proc.new { |c| c.market.retail? }, :desktop?],
 
                   unless: Proc.new { |c| c.trackpad.present? }
 
end
end
</syntaxhighlight>يتم تنفيذ هذا التحقُّق عندما تتحقق جميع الشروط في <code>if:</code>، ولا تتحقق أي من الشروط في <code>unless:</code>.


يتم تنفيذ هذا التحقُّق عندما تتحقق جميع الشروط في if:، ولا تتحقق أي من الشروط في unless:.
== تنفيذ تحققات مخصصة ==
 
عند عدم كفاية مساعدات التحقُّق المضمنة، يمكنك كتابة تحقُّقات أو توابع تحقُّق خاصة بك بناءً على حاجتك.
== تنفيذ التحقُّقات المخصّصة ==
عند عدم كفاية مساعدات التحقُّق المضمنة، يمكنك كتابة مؤكداتك أو توابع التحقُّق الخاصة بك بناءً على حاجتك.
 
=== المؤكدات المخصصة ===
المؤكدات المخصصة هي أصناف ترث من الصنف الأب ActiveModel::Validator. يجب أن تعرّف هذه الأصناف التابع validate الذي يقبل السجل المطلوب تحقُّقه وسيطًا له. يُنادى المؤكد المخصص عند استخدام الخيار validates_with.


=== عمليات التحقق المخصصة ===
عمليات التحقق المخصصة هي أصناف ترث من الصنف الأب <code>ActiveModel::Validator</code>. يجب أن تعرّف هذه الأصناف التابع <code>validate</code> الذي يقبل تمرير السجل المطلوب التحقُّق منه وسيطًا له ثم يجري عملية التحقق عليه. تستدعى علمية التحقق المخصصة باستخدام التابع <code>validates_with</code>.<syntaxhighlight lang="rails">
class MyValidator < ActiveModel::Validator
class MyValidator < ActiveModel::Validator
 
  def validate(record)
 def validate(record)
    unless record.name.starts_with? 'X'
 
      record.errors[:name] << 'Need a name starting with X please!'
   unless record.name.starts_with? 'X'
    end
 
  end
     record.errors[:name] << 'Need a name starting with X please!'
 
   end
 
 end
 
end
end
 
class Person
class Person
 
  include ActiveModel::Validations
 include ActiveModel::Validations
  validates_with MyValidator
 
 validates_with MyValidator
 
end
end
 
</syntaxhighlight>أسهل طريقة لإضافة تحقُّق مخصص للتحقُّق من خاصيات فردية هو عن طريق الصنف المريح <code>ActiveModel::EachValidator</code>. في هذه الحالة، يجب أن يعرِّف صنف التحقُّق التابع <code>validate_each</code> الذي يقبل ثلاث وسائط هي: سجل، وخاصية، وقيمة. تكون هذه الوسائط مرتبطة بالكائن، والخاصية المراد التحقُّق منها، وقيمة الخاصية المُمرَّرة للكائن.<syntaxhighlight lang="rails">
أسهل طريقة لإضافة مؤكد مخصص لتحقُّق الحقول الفردية هو عن طريق الصنف المريح ActiveModel::EachValidator. في هذه الحالة، يجب أن يعرف صنف التحقُّق التابع validate_each الذي يقبل ثلاث وسائط: السجل، الحقل، وقيمة الحقل. تكون هذه القيم مرتبطة بالكائن، والحقل الواجب تحقُّقه، وقيمة الحقل الممرة للكائن.
 
class EmailValidator < ActiveModel::EachValidator
class EmailValidator < ActiveModel::EachValidator
 
  def validate_each(record, attribute, value)
 def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
 
      record.errors[attribute] << (options[:message] || "is not an email")
   unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
    end
 
  end
     record.errors[attribute] << (options[:message] || "is not an email")
 
   end
 
 end
 
end
end
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :email, presence: true, email: true
 validates :email, presence: true, email: true
 
end
end
 
</syntaxhighlight>كما هو ظاهر في المثال، يمكنك أيضًا دمج التحقُّقات القياسية المُعرَّفة مسبقًا مع تحقُّقات مخصصة.
كما هو ظاهر في المثال، يمكنك أيضًا دمج التحقُّقات المعيارية مع تحقُّقات المخصصة.


=== التوابع المخصصة ===
=== التوابع المخصصة ===
يمكنك أيضًا التعريف عن توابع تتأكد من حالة السجلات وتضيف رسائل خطأ إلى المجموعة errors عند عدم صحة هذه السجلات. يجب عليك بعد ذلك تسجيل هذه التوابع باستخدام تابع الصنف validate، ممرّرًا رموز لأسماء هذه التوابع.
يمكنك أيضًا تعريف توابع تتحقق من حالة السجلات وتضيف رسائل خطأ إلى المجموعة <code>errors</code> عند فشل عملية التحقق. يجب عليك بعد ذلك تسجيل هذه التوابع باستخدام تابع الصنف <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate validate]</code>، ممرّرًا [[Ruby/Symbol|رموزًا]] لأسماء هذه التوابع.
 
يمكنك تمرير أكثر من رمز لكل تابع صنف، مسبوقًا بالتحقُّق المستخدم بنفس ترتيب تسجيلها.


يتحقق التابع ?valid من خلو مجموعة الأخطاء من الأخطاء، لتتمكّن توابع التحقُّق المخصصة من إضافة الأخطاء إلى المجموعة عند فشل التحقُّق:
يمكنك تمرير أكثر من رمز لكل تابع صنف، مسبوقًا بالتحقُّق المستخدم بنفس ترتيب تسجيله.


يتحقق التابع <code>?valid</code> من خلو مجموعة الأخطاء (errors collection) من الأخطاء، لتتمكّن توابع التحقُّق المخصصة من إضافة الأخطاء إلى المجموعة عند فشل التحقُّق:<syntaxhighlight lang="rails">
class Invoice < ApplicationRecord
class Invoice < ApplicationRecord
 
  validate :expiration_date_cannot_be_in_the_past,
 validate :expiration_date_cannot_be_in_the_past,
    :discount_cannot_be_greater_than_total_value
 
   :discount_cannot_be_greater_than_total_value
  def expiration_date_cannot_be_in_the_past
 
    if expiration_date.present? && expiration_date < Date.today
 def expiration_date_cannot_be_in_the_past
      errors.add(:expiration_date, "can't be in the past")
 
    end
   if expiration_date.present? && expiration_date < Date.today
  end
 
     errors.add(:expiration_date, "can't be in the past")
  def discount_cannot_be_greater_than_total_value
 
    if discount > total_value
   end
      errors.add(:discount, "can't be greater than total value")
 
    end
 end
  end
 
 def discount_cannot_be_greater_than_total_value
 
   if discount > total_value
 
     errors.add(:discount, "can't be greater than total value")
 
   end
 
 end
 
end
end
 
</syntaxhighlight>افتراضيًا، يتم تشغيل التحقُّقات المشابهة عند كل استدعاء للتابع <code>?valid</code> أو عند حفظ الكائن. لكن من الممكن أيضًا التحكم بوقت تنفيذ هذه التحقُّقات المخصصة عن طريق تحديد الخيار <code>on:</code> مع التابع <code>validate</code>، مستخدمًا إما <code>create:</code> أو <code>update:</code>.<syntaxhighlight lang="rails">
افتراضيًا، يتم تشغيل التحقُّقات المشابهة عند كل مناداة للتابع ?valid أو عند حفظ الكائن. لكن من الممكن أيضًا التحكم بوقت تنفيذ هذه التحقُّقات المخصصة عن طريق تحديد الخيار on: على التابع validate، مستخدمًا إما create: أو update:.
 
class Invoice < ApplicationRecord
class Invoice < ApplicationRecord
 
  validate :active_customer, on: :create
 validate :active_customer, on: :create
 
  def active_customer
 def active_customer
    errors.add(:customer_id, "is not active") unless customer.active?
 
  end
   errors.add(:customer_id, "is not active") unless customer.active?
 
 end
 
end
end
</syntaxhighlight>


== العمل مع أخطاء التحقُّقات ==
== العمل مع أخطاء التحققات ==
إضافةً إلى التوابع ?valid و ?invalid الموضّحة أعلاه، يزوّد Rails بمجموعة من التوابع المستخدمة للعمل مع مجموعة الأخطاء errors والحصول على معلومات حول صحة الكائنات.
إضافةً إلى التابعين <code>?valid</code> و <code>?invalid</code> الموضّحين أعلاه، يزوّد ريلز بمجموعة من التوابع المستخدمة للعمل مع مجموعة الأخطاء <code>errors</code> والحصول على معلومات حول صحة الكائنات.
 
إليك قائمة بأكثر التوابع استخدامًا. الرجاء الاطلاع على دليل ActiveModel::Errors لقائمة بالتوابع المتاحة.


=== Errors ===
إليك قائمة بأكثر التوابع استخدامًا. الرجاء الاطلاع على توثيق <code>ActiveModel::Errors</code> لقائمة بالتوابع المتاحة.
يعيد كائنًا من النوع ActiveModel::Errors يحوي جميع الأخطاء. كل مفتاح هو اسم الحقل، وكل قيمة هي مصفوفة برسائل الخطأ المرتبطة بالحقل.


=== <code>Errors</code> ===
يعيد كائنًا من النوع <code>ActiveModel::Errors</code> يحوي جميع الأخطاء. كل مفتاح هو اسم الخاصية، وكل قيمة هي مصفوفة برسائل الخطأ المرتبطة بالخاصية.<syntaxhighlight lang="rails">
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, presence: true, length: { minimum: 3 }
 validates :name, presence: true, length: { minimum: 3 }
 
end
end
 
person = Person.new
person = Person.new
person.valid? # => false
person.valid? # => false
person.errors.messages
person.errors.messages
 
# => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}
<nowiki>#</nowiki> => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}
 
person = Person.new(name: "John Doe")
person = Person.new(name: "John Doe")
person.valid? # => true
person.valid? # => true
person.errors.messages # => {}
person.errors.messages # => {}
</syntaxhighlight>


=== []errors ===
=== <code>[]errors</code> ===
يستخدم []errors عندما تريد التحقق من رسائل الخطأ لحقل محدد. يعيد هذا التابع مصفوفة من رسائل الخطأ الموافقة للحقل المعطى. في حال لم يكن هنالك أخطاء للحقل المعطى، فيعيد التابع مصفوفة فارغة.
يُستخدَم <code>[]errors</code> عندما تريد التحقق من رسائل الخطأ لخاصية محددة. يعيد هذا التابع مصفوفة من رسائل الخطأ الموافقة للخاصية المعطاة. في حال لم يكن هنالك أخطاء للحقل المعطى، فيعيد التابع مصفوفةً فارغةً.<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, presence: true, length: { minimum: 3 }
 validates :name, presence: true, length: { minimum: 3 }
 
end
end
 
person = Person.new(name: "John Doe")
person = Person.new(name: "John Doe")
person.valid? # => true
person.valid? # => true
person.errors[:name] # => []
person.errors[:name] # => []
 
person = Person.new(name: "JD")
person = Person.new(name: "JD")
person.valid? # => false
person.valid? # => false
person.errors[:name] # => ["is too short (minimum is 3 characters)"]
person.errors[:name] # => ["is too short (minimum is 3 characters)"]
 
person = Person.new
person = Person.new
person.valid? # => false
person.valid? # => false
person.errors[:name]
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
</syntaxhighlight>


<nowiki>#</nowiki> => ["can't be blank", "is too short (minimum is 3 characters)"]
=== <code>errors.add</code> ===
 
يمكّنك التابع <code>add</code> من إضافة رسائل الخطأ إلى خاصية محددة. يستقبل هذا التابع في وسائطه اسم الخاصية الواجب إضافة رسالة الخطأ عليها، ورسالة الخطأ المطلوبة.
=== errors.add ===
يمكّنك التابع add من إضافة رسائل الخطأ إلى حقل محدد. يستقبل هذا التابع في وسائطه اسم الحقل الواجب إضافة رسالة الخطأ عليه، ورسالة الخطأ المطلوبة.
 
يعيد التابع errors.full_messages (أو مثيله errors.to_a) رسائل الخطأ بنمط سهل القراءة للمستخدم، مع اسم الحقل السابق لرسالة الخطأ، كما هو ظاهر في المثال التالي.


يعيد التابع <code>errors.full_messages</code> (أو مثيله <code>errors.to_a</code>) رسائل الخطأ بنمط سهل القراءة للمستخدم، مع اسم الخاصية السابق لرسالة الخطأ، كما هو ظاهر في المثال التالي.<syntaxhighlight lang="rails">
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  def a_method_used_for_validation_purposes
 def a_method_used_for_validation_purposes
    errors.add(:name, "cannot contain the characters !@#%*()_-+=")
 
  end
   errors.add(:name, "cannot contain the characters !@#%*()_-+=")
 
 end
 
end
end
 
person = Person.create(name: "!@#")
person = Person.create(name: "!@#")
 
person.errors[:name]
person.errors[:name]
 
# => ["cannot contain the characters !@#%*()_-+="]
<nowiki>#</nowiki> => ["cannot contain the characters !@#%*()_-+="]
 
person.errors.full_messages
person.errors.full_messages
 
# => ["Name cannot contain the characters !@#%*()_-+="]
<nowiki>#</nowiki> => ["Name cannot contain the characters !@#%*()_-+="]
</syntaxhighlight>مثيل عن التابع <code>errors.add</code> هو استخدام <code>>></code> لإضافة رسالة إلى المصفوفة <code>errors.messages</code> لخاصية:<syntaxhighlight lang="rails">
 
مثيل عن التابع errors#add هو استخدام >> لإضافة رسالة إلى المصفوفة errors.messages الخاصة بالحقل:
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  def a_method_used_for_validation_purposes
 def a_method_used_for_validation_purposes
    errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
 
  end
   errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
 
 end
 
end
end
 
person = Person.create(name: "!@#")
person = Person.create(name: "!@#")
 
person.errors[:name]
person.errors[:name]
 
# => ["cannot contain the characters !@#%*()_-+="]
<nowiki>#</nowiki> => ["cannot contain the characters !@#%*()_-+="]
 
person.errors.to_a
person.errors.to_a
# => ["Name cannot contain the characters !@#%*()_-+="]
</syntaxhighlight>


<nowiki>#</nowiki> => ["Name cannot contain the characters !@#%*()_-+="]
=== <code>errors.details</code> ===
 
يمكنك تحديد نوع التحقُّق لكائن معلومات الخطأ المعاد عن طريق التابع <code>errors.add</code>.<syntaxhighlight lang="rails">
=== errors.details ===
يمكنك تحديد نوع المؤكد لكائن معلومات الخطأ المعاد عن طريق التابع errors.add.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  def a_method_used_for_validation_purposes
 def a_method_used_for_validation_purposes
    errors.add(:name, :invalid_characters)
 
  end
   errors.add(:name, :invalid_characters)
 
 end
 
end
end
 
person = Person.create(name: "!@#")
person = Person.create(name: "!@#")
 
person.errors.details[:name]
person.errors.details[:name]
 
# => [{error: :invalid_characters}]
<nowiki>#</nowiki> => [{error: :invalid_characters}]
</syntaxhighlight>لتحسين كائن معلومات الخطأ بحيث يحوي المحارف غير المسموحة على سبيل المثال، يمكنك تمرير مفاتيح إضافية للتابع <code>errors.add</code>.<syntaxhighlight lang="rails">
 
لتحسين كائن معلومات الخطأ بحيث يحوي المحارف غير المسموحة على سبيل المثال، يمكنك تمرير مفاتيح إضافية للتابع errors.add.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  def a_method_used_for_validation_purposes
 def a_method_used_for_validation_purposes
    errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=")
 
  end
   errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=")
 
 end
 
end
end
 
person = Person.create(name: "!@#")
person = Person.create(name: "!@#")
 
person.errors.details[:name]
person.errors.details[:name]
# => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}]
</syntaxhighlight>جميع التحقُّقات المدمجة في ريلز تملأ كائنات معلومات الخطأ بمجموعات متوافقة مع نوع التحقُّق المستخدم.


<nowiki>#</nowiki> => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}]
=== <code>[errors[:base</code> ===
 
يمكنك إضافة رسائل خطأ متعلقة بحالة الكائن ككل، بدلًا من أن تكون متعلقة بخاصية وحيدة. يمكنك استخدام هذا التابع عند الحاجة للقول أن الكائن ككل هو غير صحيح، بغض النظر عن قيم خاصياته. بما أن <code>[errors[:base</code> هي مصفوفة، يمكنك إضافة سلسلة نصية لها وسيتم استخدامها كرسالة خطأ.<syntaxhighlight lang="rails">
جميع مؤكدات Rails المضمنة تملأ كائنات معلومات الخطأ بمجموعات متوافقة مع نوع المؤكد المستخدم.
 
=== [errors[:base ===
يمكنك إضافة رسائل خطأ متعلقة بحالة الكائن ككل، بدلًا من أن تكون متعلقة بحقل وحيد. يمكنك استخدام هذا التابع عند الحاجة للقول أن الكائن ككل هو غير صحيح، بغض النظر عن قيم حقوله. بما أن [errors[:base هي مصفوفة، يمكنك إضافة سلسلة محرفية لها وسيتم استخدامها كرسالة خطأ.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  def a_method_used_for_validation_purposes
 def a_method_used_for_validation_purposes
    errors[:base] << "This person is invalid because ..."
 
  end
   errors[:base] << "This person is invalid because ..."
 
 end
 
end
end
</syntaxhighlight>


=== errors.clear ===
=== <code>errors.clear</code> ===
يستخدم التابع clear عندما تود بإرادتك أن تزيل جميع رسائل الخطأ من المجموعة errors. بالطبع، مناداة التابع clear على كائن غير صحيح لن يجعله صحيحًا: أي ستكون المجموعة errors فارغة، لكن عند مناداة التابع ?valid في المرة القادمة أو أي تابع يحاول حفظ الكائن في قاعدة البيانات، سيتم تنفيذ التحقُّق من جديد. وفي حال فشله، ستمتلئ المجموعة errors من جديد.
يُستخدَم التابع <code>clear</code> عندما تود بإرادتك أن تزيل جميع رسائل الخطأ من المجموعة <code>errors</code>. بالطبع، استدعاء التابع <code>clear</code> مع كائن غير صحيح لن يجعله صحيحًا: أي ستكون المجموعة <code>errors</code> فارغة، لكن عند استدعاء التابع <code>?valid</code> في المرة القادمة أو أي تابع يحاول حفظ الكائن في قاعدة البيانات، فسيتم تنفيذ التحقُّق من جديد. وفي حال فشله، ستمتلئ المجموعة <code>errors</code> من جديد.<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, presence: true, length: { minimum: 3 }
 validates :name, presence: true, length: { minimum: 3 }
 
end
end
 
person = Person.new
person = Person.new
person.valid? # => false
person.valid? # => false
person.errors[:name]
person.errors[:name]
 
# => ["can't be blank", "is too short (minimum is 3 characters)"]
<nowiki>#</nowiki> => ["can't be blank", "is too short (minimum is 3 characters)"]
 
person.errors.clear
person.errors.clear
person.errors.empty? # => true
person.errors.empty? # => true
 
person.save # => false
person.save # => false
 
person.errors[:name]
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
</syntaxhighlight>


<nowiki>#</nowiki> => ["can't be blank", "is too short (minimum is 3 characters)"]
=== <code>errors.size</code> ===
 
يعيد التابع <code>size</code> عدد رسائل الخطأ للكائن.<syntaxhighlight lang="rails">
=== errors.size ===
يعيد التابع size عدد رسائل الخطأ للكائن.
 
class Person < ApplicationRecord
class Person < ApplicationRecord
 
  validates :name, presence: true, length: { minimum: 3 }
 validates :name, presence: true, length: { minimum: 3 }
 
end
end
 
person = Person.new
person = Person.new
person.valid? # => false
person.valid? # => false
person.errors.size # => 2
person.errors.size # => 2
 
person = Person.new(name: "Andrea", email: "andrea@example.com")
person = Person.new(name: "Andrea", email: "andrea@example.com")
person.valid? # => true
person.valid? # => true
person.errors.size # => 0
person.errors.size # => 0
</syntaxhighlight>


== عرض أخطاء التحقُّق في الواجهات ==
== عرض أخطاء التحقق في الواجهات ==
عند إنشائك لسجل وتحقُّقه، في حال تم إنشاء هذا السجل عن طريق نموذج ويب، قد يكون من الضروري إظهار رسالة الخطأ عند فشل التحقُّق.
متى ما أنشأت نموذجًا وأضفت تحقُّقات له، ففي حال تم إنشاء هذا النموذج (model) عن طريق نموذج ويب (web form)، فقد يكون من الضروري إظهار رسالة الخطأ عند فشل التحقُّق.
 
لأن كل تطبيق يراعي هذه الحالة بطريقة مختلفة، لا يزود Rails بأي مساعد واجهة للمساعدة بتوليد هذه الرسائل مباشرةً. لكن نظرًا للعدد الكبير من التوابع المزودة من Rails للتفاعل مع التحقُّقات بشكل عام، من السهل بناء هذه التوابع بيدك. علاوةً على ذلك، عند توليد مجموعة، يقوم Rails بوضع تعليمات ERB في الملف form.html.erb_ المولّد، والذي يظهر القائمة الكاملة بأخطاء هذا السجل.


بفرض أنه لدينا سجل تم حفظه في كائن يسمى article@، يبدو الأمر كذلك:
لأن كل تطبيق يراعي هذه الحالة بطريقة مختلفة، لا يزود ريلز بأي مساعد واجهة للمساعدة بتوليد هذه الرسائل مباشرةً. لكن نظرًا للعدد الكبير من التوابع المزودة من ريلز للتفاعل مع التحقُّقات بشكل عام، من السهل بناء هذه التوابع يدويًّا. علاوةً على ذلك، عند توليد scaffold (هو جزء من النمط البرمجي MVC حيثُ يُمكنك إختيار جزء مُعين من قاعدة البيانات للعمل عليه)، يقوم ريلز بوضع تعليمات ERB في الملف form.html.erb_ المولّد، والذي يظهر القائمة الكاملة بأخطاء هذا النموذج.


بفرض أنه لدينا نموذجًا تم حفظه في متغير نسخة تسمى <code>article@</code>، يبدو الأمر كذلك:<syntaxhighlight lang="rails">
<% if @article.errors.any? %>
<% if @article.errors.any? %>
 
  <div id="error_explanation">
 <nowiki><div id="error_explanation"></nowiki>
    <h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2>
 
   <nowiki><h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2></nowiki>
    <ul>
 
    <% @article.errors.full_messages.each do |msg| %>
   <nowiki><ul></nowiki>
      <li><%= msg %></li>
 
    <% end %>
   <% @article.errors.full_messages.each do |msg| %>
    </ul>
 
  </div>
     <nowiki><li><%= msg %></li></nowiki>
 
   <% end %>
 
   <nowiki></ul></nowiki>
 
 <nowiki></div></nowiki>
 
<% end %>
<% end %>
 
</syntaxhighlight>من ناحية أخرى، في حال كنت تستخدم مساعدات ريلز لإنشاء نماذج الويب، عند فشل تحقُّق على حقل معين، ستولّد هذه المساعدات عنصر <code>[[HTML/div|<nowiki><div></nowiki>]]</code> إضافي حول الحقل.<syntaxhighlight lang="rails">
من ناحية أخرى، في حال كنت تستخدم مساعدات Rails لإنشاء نماذج الويب، عند فشل تحقُّق على حقل معين، ستولّد هذه المساعدات عنصر <nowiki><div> إضافي حول الحقل.</nowiki>
<div class="field_with_errors">
 
<input id="article_title" name="article[title]" size="30" type="text" value="">
<nowiki><div class="field_with_errors"></nowiki>
</div>
 
</syntaxhighlight>يمكنك بناءً على ذلك تعديل شكل هذا العنصر كما تشاء. الشكل الافتراضي المضاف من vdg. مثلًا، يقوم بإنشاء قواعد CSS التالية:<syntaxhighlight lang="rails">
<input id="article_title" name="article[title]" size="30" type="text" value="">
 
<nowiki></div></nowiki>
 
يمكنك بناءً على ذلك تعديل شكل هذا العنصر كما تشاء. الشكل الافتراضي المضاف من Rails مثلًأ، يقوم بإنشاء قواعد CSS التالية:
 
.field_with_errors {
.field_with_errors {
 
  padding: 2px;
 padding: 2px;
  background-color: red;
 
  display: table;
 background-color: red;
 
 display: table;
 
}
}
</syntaxhighlight>ممّا يعني أن أي حقل يملك أخطاءً سيتم تلوين حدوده باللون الأحمر بسماكة 2 بكسل.


ممّا يعني أن أي حقل يملك أخطاءً سيتم تلوين حدوده باللون الأحمر بسماكة 2 بكسل.
== مصادر ==
* [https://guides.rubyonrails.org/active_record_validations.html#custom-validators صفحة Active Record Validations في توثيق Ruby On Rails الرسمي.]

المراجعة الحالية بتاريخ 09:23، 24 مارس 2019

يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال ميزة التحققات من الصحة (validations) التي يوفرها Active Record. بعد قراءة هذا الدليل، ستتعرَّف على:

  • كيفية استعمال مساعدي التحقق من الصحة (validation helpers) لـ Active Record المدمجين.
  • كيفية إنشاء توابع مخصصة للتحقق من الصحة.
  • كيفية العمل مع رسالة الخطأ المولدة عبر عملية التحقق.

نظرة عامة على عمليات التحقق

الشيفرة التالية تظهر مثالًا عن تحقق بسيط جدًا:

class Person < ApplicationRecord
  validates :name, presence: true
end
 
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

كما يمكنك أن ترى، تمكنّنا التحقُّقات من معرفة أن الكائن Person ليس صحيحًا بدون اسم؛ مما يعني أن الكائن Person الثاني لن يتم حفظه في قاعدة البيانات.

قبل أن ندخل في التفاصيل، لنتحدث عن أهمية التحقُّقات في تطبيقك.

لمَ نستخدم التحقُّقات؟

تستخدم التحقُّقات للتأكّد من أنّ البيانات التي يتم حفظها في قاعدة البيانات هي صحيحة حتمًا. على سبيل المثال، قد يكون من الضروري على تطبيقك التأكّد من أنّ كل مستخدم يزوّدك ببريده الالكتروني وعنوانه الخاص. التحقُّقات على مستوى النماذج هي الطريقة الأمثل للتأكد من أنّ البيانات الصحيحة تمامًا يتم حفظها في قاعدة البيانات. تعتبر التحقُّقات مستقلة عن قاعدة البيانات، ولا يمكن تجاوزها من المستخدم النهائي، وتتمتّع بأريحية بالصيانة والاختبار. يجعل ريلز التحقُّقات سهلة الاستخدام، ويزوّد بمساعدات مدمجة من أجل الحاجات المختلفة، ويمكّنك من كتابة توابع التحقُّق الخاصة بك أيضًا.

هناك مجموعة من الطرق للتأكّد من البيانات قبل حفظها في قاعدة البيانات، بما فيها القيود الأصلية لقاعدة البيانات، وتحقُّقات من طرف المستخدم وتحقُّقات من طرف المتحكّم. إليك شرح قصير عن إيجابيات وسلبيّات كلّ منها:

  • إن قيود قاعدة البيانات أو الإجراءات المضمَّنة (Stored Procedures) تجعل هيكلية التحقُّق معتمدة تمامًا على قاعدة البيانات، وتصعّب من عمليّة الاختبار والصيانة. لكن في حال كانت قاعدة البيانات الخاصة بك مستخدمة من قبل تطبيقات أخرى، فقد يكون من الضروري استخدام قيود قاعدة البيانات. علاوةً على ذلك، تستطيع قيود قاعدة البيانات معالجة بعض الحالات بشكل آمن وسلس (كالحقول الفريدة في الجداول كثيرة البيانات)، والتي من الصعب تنفيذها بطرق أخرى.
  • يمكن للتحقُّقات من طرف المستخدم (Client-side validations) أن تكون مفيدة، لكن لا يمكن الاعتماد عليها في حال تم تطبيقها بمفردها فقط. في حال تم تطبيقها باستخدام JavaScript، يستطيع المستخدم تجاوزها في حال قام بإلغاء JavaScript في المستعرض الخاص به. لكن في حال تم دمجها مع تقنيات أخرى، يمكن للتحقُّقات من طرف المستخدم أن تكون طريقة مريحة وسهلة لتزويد المستخدم بتغذية راجعة آنية أثناء استخدامه لموقعك.
  • يمكن للتحقُّقات التي على مستوى المتحكّم (Controller-level validations) أن تكون مشوّقة للاستخدام، لكن تصبح بعض الأوقات غير عملية وصعبة للاختبار والصيانة. عند الإمكانية، يجب على متحكّماتك أن تبقى مجرّدة ومختصرة، ممّا يجعل العمل على تطبيقك أمرًا سهلًا مع مرور الوقت.

يمكن استخدام هذه الطرق في حالات مخصصة. برأي فريق عمل ريلز، طريقة التحقُّقات في مستوى النماذج (model-level validations) هي الطريقة المثلى في معظم الحالات.

متى يحدث التحقق؟

هناك نوعان من كائنات Active Record: الكائنات التي تتفاعل مع سجل من قاعدة بياناتك، والكائنات التي لا تتفاعل. عند إنشائك لكائن جديد، مثلًا باستخدام التابع new، لن يتم حفظ هذا الكائن في قاعدة البيانات بعد. بعد استدعائك للتابع save على هذا الكائن، سيتم حفظه في جدول قاعدة البيانات. يستخدم Active Record تابع النسخة ?new_record للتأكّد من وجود السجل في قاعدة البيانات أم لا. لنطلع على نموذج Active Record البسيط التالي:

class Person < ApplicationRecord
end

يمكننا أن نرى كيف يعمل عن طريق الاطلاع على خرج الأمر rails console:

$ bin/rails console
>> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
>> p.new_record?
=> true
>> p.save
=> true
>> p.new_record?
=> false

إنّ إنشاء وحفظ سجل جديد سيرسل العملية SQL INSERT لقاعدة البيانات. وإن تعديل سجل موجود مسبقًا سيرسل العملية SQL UPDATE بدلًا من ذلك. تُشغَّل التحقُّقات عادةً قبل إرسال هذه الأوامر إلى قاعدة البيانات. في حال فشل أحد التحقُّقات، سيتم تعيين الكائن ككائن غير صحيح ولن يتم إرسال الاستعلام INSERT أو UPDATE. يحدّ هذا من حفظ كائنات غير صحيحة في قاعدة البيانات. يمكنك تحديد فيما إذا أردت تنفيذ تحقُّقات معينة عند إنشاء كائن أو تعديله أو حفظه.

تحذير: هناك طرائق عدَّة لتغيير حالة كائن ما في قاعدة البيانات. تشغّل بعض التوابع عمليات التحقق، والبعض الآخر لا يفعل ذلك. ممّا يعني أنّه يمكنك حفظ كائن ما في قاعدة البيانات في حالة غير صحيحة إن لم تكن على دراية.

تشغّل التوابع التالية عمليات التحقق، وتحفظ الكائن في قاعدة البيانات فقط عند كونه صحيحًا:

  • create
  • create!‎
  • save
  • save!‎
  • update
  • update!‎

ترمي التوابع ذات الإصدار المُغيِّر (bang version مثل التابع !save) استثناءات عند عدم صحة الكائنات. بينما لا تفعل ذلك التوابع ذات الإصدار غير المُغيِّر (non-bang versions) مثل التابعان save و update اللذان يعيدان القيمة false، والتابع create يعيد الكائن نفسه.

تجاوز التحقُّقات

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

  • decrement!‎
  • decrement_counter
  • increment!‎
  • increment_counter
  • toggle!‎
  • touch
  • update_all
  • update_attribute
  • update_column
  • update_columns
  • update_counters

تجب الملاحظة أنّ للتابع save إمكانية تجاوز التحقُّقات أيضًا، عن طريق تمرير القيمة false للوسيط validate. بالمثل، يجب استخدام هذه الطريقة بحذر:

  • save(validate: false)‎

التابعان ?valid و ?invalid

قبل حفظ كائن Active Record، يشغّل ريلز التحقُّقات الخاصة بك. في حال أثمرت هذه التحقُّقات أخطاءً، فلن يحفظ ريزل الكائن.

يمكنك أيضًا تشغيل التحقُّقات يدويًا. إذ يقوم التابع ?valid بتشغيل التحقق ويعيد القيمة true في حال نجاحه، والقيمة false عدا عن ذلك. كما رأيت أعلاه:

class Person < ApplicationRecord
  validates :name, presence: true
end
 
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

بعد قيام كائن Active Record بالتحقُّقات، يمكن الوصول لرسائل الخطأ عن طريق تابع النسخة errors.messages، الذي يعيد مجموعة من الأخطاء. من التعريف، يكون الكائن صحيحًا في حال كانت هذه المجموعة خالية بعد تنفيذ التحقُّق. تجدر الملاحظة أنَّ الكائن الذي يتم تهيئته باستخدام new لن يعطي أي خطأ حتّى لو كان غير صالح، لأنَّ التحقُّقات تنفّذ تلقائيًا عند حفظ الكائن، كما هو الحال في التابعين create و save.

class Person < ApplicationRecord
  validates :name, presence: true
end
 
>> p = Person.new
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {}
 
>> p.valid?
# => false
>> p.errors.messages
# => {name:["can't be blank"]}
 
>> p = Person.create
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {name:["can't be blank"]}
 
>> p.save
# => false
 
>> p.save!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
 
>> Person.create!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

التابع ?invalid هو ببساطة عكس التابع ?valid، إذ ينفّذ التحقُّق، ويعيد القيمة true في حال وجود أخطاء في الكائن، و false فيما عدا ذلك.

التابع []errors

للتأكد من صحة حقل ما في كائن، يمكنك استخدام التابع [errors[:attribute، حيث attribute: هو الحقل المطلوب التحقق منه. يعيد هذا التابع مصفوفةً من الأخطاء من أجل الحقل المطلوب، وفي حال عدم وجود أخطاء فيتم إعادة مصفوفة فارغة.

يفيد التابع هذا فقط بعد تنفيذ التحقُّقات، لأنّه فقط يقوم بقراءة مصفوفة الأخطاء ولا ينفّذ التحقُّقات بحد ذاتها. ويعتبر مختلفًا عن التابع ActiveRecord::Base.invalid الموضح أعلاه لأنّه لا يعبّر عن صحّة كائن كامل، بل يحتوي فقط على أخطاء الحقل المطلوب من الكائن.

class Person < ApplicationRecord
  validates :name, presence: true
end
 
>> Person.new.errors[:name].any? # => false
>> Person.create.errors[:name].any? # => true

سنقوم بتغطية أخطاء التحقُّقات بشكل كامل في قسم العمل مع أخطاء التحقُّقات.

الخاصية errors.details

للتأكد من التحقُّقات التي فشلت على حقل ما، يمكنك استخدام [errors.details[:attribute. يعيد هذا التابع مصفوفة من القيم hash مع المفتاح error: لجلب رمز التحقُّق:

class Person < ApplicationRecord
  validates :name, presence: true
end
 
>> person = Person.new
>> person.valid?
>> person.errors.details[:name] # => [{error: :blank}]

إن استخدام details مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات.

مساعدو التحقق

يزوّد Active Record مجموعةً معرفةً مسبقًا من المساعدين الذين يمكنك استخدامهم بشكل مباشر في تعريفات الأصناف الخاصة بك. تزوّد هؤلاء المساعدين بقواعد متعارف عليها لإجراء التحقُّقات. في كل مرة يفشل فيها تحقُّق ما، سيتم إضافة رسالة خطأ إلى المجموعة errors الخاصة بالكائن، وتضم هذه الرسالة اسم الحقل الذي فشل التحقُّق منه.

يقبل كل مساعد مجموعة من أسماء الحقول، أي يمكنك في سطر تعريف وحيد إضافة نفس التحقُّق لمجموعة من الحقول.

جميعهم يقبلون الخيارين on: و message:، اللذَين يحددان متى يتم تشغيل التحقُّقات وما الرسالة الواجب إضافتها في حال فشل التحقُّق. يقبل الخيار on: إحدى القيمتين create: أو update:. هناك رسائل افتراضية لكل نوع من أنواع التحقُّقات، والتي يتم استخدامها عند عدم تحديد رسالة بشكل يدوي في الخيار message:. لنطلّع على كل مساعد من هؤلاء المساعدين.

المساعد acceptance

يأكّد هذا المساعد أن صندوق الاختيار (checkbox) تم تحديده على واجهة المستخدم عند إرسال النموذج. يستخدم هذا المساعد بشكل تقليدي عند حاجة المستخدم للموافقة على شروط استخدام التطبيق مثلَا، أو للتأكد من قراءة نص معين، أو أي شيء مماثل.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: true
end

يُنفّذ هذا التحديد فقط عندما يكون terms_of_service ليس nil. إن رسالة الخطأ الافتراضية لهذا المساعد هي "must be accepted". يمكنك تمرير رسالة يدوية عن طريق الخيار message:.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { message: 'must be abided' }
end

يمكن أيضًا تمرير الخيار accept: الذي يحدد القيم التي يجب اعتبارها خيارًا مقبولًا. القيمة الافتراضية لهذا الخيار هي [1,true] ويمكن تغييرها بسهولة:

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { accept: 'yes' }
  validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
end

هذا التحقُّق مخصَّص لتطبيقات الويب وليس من الضروري تخزين هذا القبول في أي مكان في قاعدة البيانات. في حال لم تكن تملك هذا الحقل في قاعدة البيانات، يقوم المساعد بإنشائه لك بشكل افتراضي. في حال عدم وجود الحقل في قاعدة البيانات، يجب ضبط الخيار accept أو أن يحوي القيمة true، وإلا لن يتم تشغيل التحقُّق.

المساعد validates_associated

يمكنك استخدام هذا المساعد في حال كان نموذجك يملك ارتباطات مع نماذج أخرى تحتاج للتحقُّق أيضًا. عند محاولتك حفظ السجل، سيتم استدعاء التابع ?valid على كل كائن من الكائنات المرتبطة أيضًا.

class Library < ApplicationRecord
  has_many :books
  validates_associated :books
end

سيعمل هذا التحقُّق على جميع الأنواع المرتبطة.

تحذير: لا تستخدم المساعد validates_associated على طرفي الارتباط، إذ سيؤدي ذلك إلى استدعاء أحدهما للآخر مما يؤدي إلى الدخول في حلقة لا نهائية.

إن رسالة الخطأ الافتراضية لهذا التحقُّق هي "is invalid". تجب الملاحظة أن لكل كائن من الكائنات المرتبطة مجموعة الأخطاء الخاصة به، ولن يتم حفظ مجموعة الأخطاء للكائنات الأبناء في الكائن المستدعي.

المساعد confirmation

يجب أن تستخدم هذا المساعد عند وجود حقلين نصيَّين يجب على كل منهما أن يرسلا نفس القيمة تمامًا. مثلًا، قد تحتاج إلى تحقُّق بريد الكتروني أو كلمة مرور. ينشئ هذا التحقُّق حقلًا افتراضيًا ويسمّيه بنفس اسم الحقل الأساسي، يليه العبارة "confirmation_".

class Person < ApplicationRecord
  validates :email, confirmation: true
end

ويجب أن يبدو نموذج العرض الخاص بك كالتالي:

<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>

ينفّذ هذا التحقُّق فقط عند كون email_confirmation ليس nil. لطلب التحقُّق بشكل قسري، لا تنسَ إضافة التحقُّق presence للخاصية confirmation (سنطلع على التحقُّق presence لاحقًا في هذا التوثيق):

class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true
end

يمكنك أيضًا تمرير الخيار case_sensitive: لتحديد فيما إذا كان القيد confirmation  حساسًا لحالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي true.

class Person < ApplicationRecord
  validates :email, confirmation: { case_sensitive: false }
end

رسالة الخطأ الافتراضية لهذا المساعد هي "doesn't match confirmation".

المساعد exclusion

يتأكّد هذا المساعد من أن قيم الحقل المعطى ليست موجودة ضمن المجموعة المحددة. يمكن أن تكون هذه المجموعة أيًّا من الكائنات القابلة للتعداد (enumerable object).

class Account < ApplicationRecord
  validates :subdomain, exclusion: { in: %w(www us ca jp),
    message: "%{value} is reserved." }
end

يملك المساعد exclusion الخيار in: الذي يقبل مجموعة من القيم التي لن يتم قبولها لخاصيات المتحقق منها. يمكن كتابة in: أيضًا بالشكل within:. يستخدم هذا المثال الخيار message: ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية يمكنك الاطلاع على قسم الرسائل في الأسفل.

رسالة الخطأ الافتراضية لهذا المساعد هي "is reserved".

المساعد format

يؤكد هذا المساعد أن قيمة الحقل المعطى توافق التعبير النمطي المحدد بالخيار with: كالتالي:

class Product < ApplicationRecord
  validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
    message: "only allows letters" }
end

بالمثل، يمكنك تحديد أن قيمة الحقل المعطى لا توافق التعبير النمطي المحدد، وذلك بالخيار without:.

رسالة الخطأ الافتراضية لهذا المساعد هي "is invalid".

المساعد inclusion

يتأكد هذا المساعد من أن قيم الحقل المعطى موجودة ضمن المجموعة المحددة:

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }
end

يملك المساعد inclusion الخيار in: الذي يستقبل مجموعة من القيم المراد قبولها فقط. يمكن كتابة in: أيضًا بالشكل within:. يستخدم هذا المثال الخيار message: ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية، يمكنك الاطلاع على قسم الرسائل في الأسفل.

رسالة الخطأ الافتراضية لهذا المساعد هي "is not included in the list".

المساعد length

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

class Person < ApplicationRecord
  validates :name, length: { minimum: 2 }
  validates :bio, length: { maximum: 500 }
  validates :password, length: { in: 6..20 }
  validates :registration_number, length: { is: 6 }
end

الخيارات الممكنة لقيود الطول هي:

  • minimum:: لا يمكن للحقل أن يحوي عدد محارف أقل من الطول المعطى.
  • maximum:: لا يمكن للحقل أن يحوي عدد محارف أكثر من الطول المعطى.
  • in: (أو within:): يمكن للحقل أن يحوي عدد محارف بطول موجود ضمن المجال المعطى.
  • is:: يجب على الحقل أن يحوي عدد محارف يساوي الطول المعطى.

تعتمد رسالة الخطأ الافتراضية على نوع تحقُّق الطول المستخدم. يمكنك تعديل هذه الرسائل عن طريق الخيارات wrong_length:، و too_long:، و too_short:، واستخدام {count}% لاستيفاء طول المحارف المستخدم. يمكنك استخدام الخيار message: لتحديد رسالة خطأ.

class Person < ApplicationRecord
  validates :bio, length: { maximum: 1000,
    too_long: "%{count} characters is the maximum allowed" }
end

تجب الملاحظة أن رسائل الخطأ الافتراضية هي في صيغة الجمع مثل:

"is too short (minimum is %{count} characters)"

لهذا السبب، عند كون minimum: تساوي 1، يجب تزويد رسالة معدلة أو استخدام الخيار presence: true بدلًا من ذلك. عندما يملك الخيار in: أو within: حدًا أصغريًا مساويًا للواحد، يجب عليك تزويد رسالة معدلة أو استخدام الخيار presence قبل length.

المساعد numericality

يتأكد هذا المساعد من أن الحقل يحتوي فقط على قيم رقمية. افتراضيًا، يطابق هذا المساعد إشارة الرقم الاختيارية، متبوعةً بعدد صحيح أو عشري. لتحديد قبول الأعداد الصحيحة فقط، يمكن تعيين الخيار only_integer: إلى true.

وفي حال ضبط only_integer: إلى القيمة true، فسيتم استخدام التعبير النمطي التالي:

/\A[+-]?\d+\z/

وذلك للتحقُّق من قيمة الخاصية. فيما عدا ذلك، سيحاول تحويل قيمة الحقل إلى عدد باستخدام Float.

class Player < ApplicationRecord
  validates :points, numericality: true
  validates :games_played, numericality: { only_integer: true }
end

إلى جانب only_integer:، يقبل هذا المساعد مجموعة من الخيارات لإضافة قيود على القيم المقبولة:

  • الخيار greater_than:: يحدد أن القيمة يجب أن تكون أكبر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than %{count}‎".
  • الخيار greater_than_or_equal_to:: يحدد أن القيمة يجب أن تكون أكبر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than or equal to %{count}‎".
  • الخيار equal_to:: يحدد أن القيمة يجب أن تكون تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be equal to %{count}‎"
  • الخيار less_than:: يحدد أن القيمة يجب أن تكون أصغر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than %{count}‎".
  • الخيار less_than_or_equal_to:: يحدد أن القيمة يجب أن تكون أصغر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than or equal to %{count}‎".
  • الخيار other_than:: يحدد أن القيمة يجب أن تكون غير مساوية للقيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be other than %{count}‎"
  • الخيار odd:: يحدد أن القيمة يجب أن تكون عددًا فرديًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be odd".
  • الخيار even:: يحدد أن القيمة يجب أن تكون عددًا زوجيًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be even".

ملاحظة: افتراضيًا، لا يسمح المساعد numericality بالقيم nil. يمكنك استخدام الخيار allow_nil: للسماح بها.

الرسالة الافتراضية لهذا المساعد هي "is not a number".

المساعد presence

يتحقق هذا المساعد من أن عدم خلو خاصيات محدَّدة من أية قيمة. يستخدم هذا المساعد التابع ?blank للتأكد من أن القيمة هي nil، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.

class Person < ApplicationRecord
  validates :name, :login, :email, presence: true
end

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

class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, presence: true
end

من أجل تحقُّق السجلات المرتبطة الضروري وجودها، يجب تحديد الخيار inverse_of: من أجل الارتباط:

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

في حال قمت بالتحقُّق من وجود كائن مرتبط عن طريق العلاقة has_one أو has_many، سيتفقّد من كون الكائن غير محقق للتابع ?blank ولا التابع ?marked_for_destruction. لمَّا كان ?false.blank يعيد القيمة true، فإذا أردت التحقُّق من وجود حقل من النوع Boolean، فيجب عليك القيام بأحد التحققين التاليين:

validates :boolean_field_name, inclusion: { in: [true, false] }
validates :boolean_field_name, exclusion: { in: [nil] }

باستخدام أحد هذين التحقُّقين، يمكنك التأكد من عدم استقبال الحقل المنطقي القيمة nil.

المساعد absence

يؤكد هذا المساعد غياب وعدم وجود قيم خاصية محددة. يستخدم هذا المساعد التابع ?present للتأكد من أن القيمة هي ليست nil، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.

class Person < ApplicationRecord
  validates :name, :login, :email, absence: true
end

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

class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, absence: true
end

من أجل تحقُّق السجلات المرتبطة الضروري غيابها، يجب تحديد الخيار inverse_of: من أجل الارتباط:

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

في حال قمت بالتحقُّق من غياب كائن مرتبط عن طريق علاقة has_one أو has_many، سيتفقّد من أنَّ الكائن غير محقق للتابع ?present ولا التابع ?marked_for_destruction. لما كان ?false.present يعيد القيمة false، فإذا أردت التحقُّق من غياب حقل من النوع المنطقي، فيجب عليك استعمال:

validates :field_name, exclusion: { in: [true, false] }

رسالة الخطأ الافتراضية لهذا المساعد هي "must be blank".

المساعد uniqueness

يتحقق هذا المساعد من أنَّ قيمة الحقل فريدة وغير مكررة قبل حفظ الكائن. لا ينشئ هذا المساعد قيد التميز في قاعدة البيانات، لذلك قد تحدث حالة إضافة سجلّين بقيمتين متساويتين في حقل واحد تريده أن يكون فريدًا، وذلك من قبل اتصالين منفصلين مع قاعدة البيانات. لتفادي هذا، يجب عليك إنشاء فهرس فريد لهذا الحقل في قاعدة البيانات.

class Account < ApplicationRecord
  validates :email, uniqueness: true
end

يجري تنفيذ التحقُّق عن طريق تنفيذ استعلام SQL على جدول النموذج، بحثًا عن سجل موجود يملك ذات القيمة في الحقل المعطى. يمكن استخدام الخيار scope: لتحديد خاصية أو أكثر مستخدمة للتحقق uniqueness:

class Holiday < ApplicationRecord
  validates :name, uniqueness: { scope: :year,
    message: "should happen once per year" }
end

إذا أردت إنشاء قيدٍ على قاعدة البيانات لتفادي تجاوز التحقُّقات uniqueness المستخدمة للخيار scope:، يجب أن تنشئ فهرسًا فريدًا في كلا الحقلين في قاعدة البيانات. اقرأ دليل MySQL للمزيد من المعلومات حول إنشاء فهرس لحقول متعددة أو دليل PostgreSQL لأمثلة حول قيود التميز المستخدمة على نطاق مجموعة من الحقول في آن معًا. هناك أيضًا الخيار case_sensitive: لتحديد فيما إذا كان القيد uniqueness يراعي حالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي true.

class Person < ApplicationRecord
  validates :name, uniqueness: { case_sensitive: false }
end

ملاحظة: بعض قواعد البيانات مهيّئة لتنفيذ عمليات البحث غير الحساسة لحالة الأحرف في كلتا الحالتين.

رسالة الخطأ الافتراضية لهذا المساعد هي "has already been taken".

المساعد validates_with

يمرر هذا المساعد السجل الواجب التحقق منه إلى صنف آخر منفصل للقيام بعملية التحقُّق.

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.first_name == "Evil"
      record.errors[:base] << "This person is evil"
    end
  end
end
 
class Person < ApplicationRecord
  validates_with GoodnessValidator
end

ملاحظة: الأخطاء المضافة إلى [record.errors[:base تتعلق بحالة السجل ككل، وليس بحقل معين.

يقبل المساعد validates_with صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق الصنف validator.

لكتابة تابع التحقُّق، يجب أن تستقبل وسيطًا يمثل السجل المطلوب تحقُّقه.

كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات if: و unless: و on:. إذا مرّرت خيارات إضافية، فسيتم تمريرها إلى الصنف validator ضمن الوسيط options:

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if options[:fields].any?{|field| record.send(field) == "Evil" }
      record.errors[:base] << "This person is evil"
    end
  end
end
 
class Person < ApplicationRecord
  validates_with GoodnessValidator, fields: [:first_name, :last_name]
end

من الجدير بالملاحظة أنّه يتم تهيئة الصنف validator مرّة واحدة فقط ضمن دورة حياة التطبيق ككل، وليس في كل مرة يتم تنفيذ تحقُّق معيّن؛ لذلك كن حذرًا أثناء التعامل مع متغيرات النسخة داخله. إذا كان صنف التحقُّق validator الخاص بك معقدًا لدرجة حاجته لمتغيّرات نسخة، فيمكنك استخدام كائنات روبي البسيطة بدلًا من ذلك:

class Person < ApplicationRecord
  validate do |person|
    GoodnessValidator.new(person).validate
  end
end
 
class GoodnessValidator
  def initialize(person)
    @person = person
  end
 
  def validate
    if some_complex_condition_involving_ivars_and_private_methods?
      @person.errors[:base] << "This person is evil"
    end
  end
 
  # ...
end

المساعد validates_each

يتحقَّق هذا المساعد من مجموعة من الخاصيات مقابل كتلة من التعليمات البرمجية، إذ لا يملك تابع تحقُّق معرف مسبقًا. يجب عليك إنشاء هذا التابع باستخدام كتلة، وكل خاصية ممرّرة للمساعد validates_each سيتم اختبارها في هذه الكتلة. في المثال التالي، لا نريد من الأسماء الأولى والكنيات أن تبدأ بأحرف صغيرة.

class Person < ApplicationRecord
  validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
  end
end

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

خيارات عملية التحقق الشائعة

إليك مجموعة من خيارات التحقُّق الشائعة المستخدمة في Active Record:

الخيار allow_nil:

يتجاوز الخيار allow_nil: التحقُّق عندما تكون القيمة الواجب التحقق منها هي nil.

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }, allow_nil: true
end

لقراءة كل الخيارات حول الوسيط message، اطّلع على التوثيق الخاص به.

الخيار allow_blank:

يشابه هذا الخيار الخيار allow_nil:، إذ يتجاوز التحقُّق في حال كانت قيمة الخاصية هي ?blank، مثل القيمة nil أو سلسلة نصية فارغة.

class Topic < ApplicationRecord
  validates :title, length: { is: 5 }, allow_blank: true
end
 
Topic.create(title: "").valid?  # => true
Topic.create(title: nil).valid? # => true

الخيار message:

كما رأيت مسبقًا، يمكّنك الخيار message: من تحديد رسالة خطأ يراد إضافتها لمجموعة الأخطاء errors عند فشل عملية التحقُّق. عند عدم تحديد هذا الخيار، يرسل Active Record رسالة الخطأ الافتراضية من أجل كل مساعد تحقُّق. يقبل الخيار message: إما سلسلة نصية أو كائنًا من النوع Proc.

يمكن للسلسلة النصية الممررة للخيار message: أن تحوي كل من {value}% أو {attribute}% أو {model}% التي يتم استبدالها دينياميكيًا عند فشل التحقُّق. يتم تنفيذ هذا الاستبدال باستخدام الجوهرة I18n.

أمَّا في حال استعمال الكائن Proc مع الخيار message:، يتم تمرير وسيطين هما: الكائن المراد التحقق منه، ومجموعة بأزواج المفاتيح والقيم التالية: model: و attribute: و value:.

class Person < ApplicationRecord
  # رسالة ثابتة
  validates :name, presence: { message: "must be given please" }
 
  # %{value} رسالة مع قيمة ديناميكية.  يتم استبدال
  # متاحتان أيضًا %{model} و %{attribute} بالقيمة الحقيقية للحقل. إن
  validates :age, numericality: { message: "%{value} seems wrong" }
 
  # Proc
  validates :username,
    uniqueness: {
      # object = الكائن المراد التحقق منه
      # data = { model: "Person", attribute: "Username", value: <username> }
      message: ->(object, data) do
        "Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}"
      end
    }
end

الخيار on:

يمكّنك هذا الخيار من تحديد مكان التحقُّق. السلوك الافتراضي لجميع مساعدات التحقُّق المضمنة هو تشغيل التحقُّق عند الحفظ (أي عند إنشاء أو تحديث السجلات). إذا أردت تغيير هذا السلوك، فيمكنك تحديد الخيار on: على create: لتشغيل التحقُّق فقط عند إنشاء كائن جديد، أو على update: لتشغيل التحقُّق فقط عند تعديل كائن موجود مسبقًا.

class Person < ApplicationRecord
  # سيكون بالإمكان تحديث  عنوان البريد الإلكتروني مع قيمة مكررة
  validates :email, uniqueness: true, on: :create
 
  # سيكون بالإمكان إنشاء سجل  مع قيمة غير عددية للعمر
  validates :age, numericality: true, on: :update
 
  # القيمة الافتراضية هي التحقق عند الإنشاء والتحديث
  validates :name, presence: true
end

يمكنك أيضًا استخدام الخيار on: لتعريف سياق مخصّص. يجب تشغيل السياق المخصص بشكل ظاهري عن طريق تمرير اسم السياق إلى التوابع ?valid أو ?invalid أو save.

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
end
 
person = Person.new

يتم تنفيذ (person.valid?(:account_setup عند التحقُّقين دون حفظ السجل. ويقوم التابع (person.sav(context: :account_setup بالتحقُّق من الكائن person في السياق account_setup قبل الحفظ. عند التشغيل بشكل ظاهري، يتم التحقُّق من السجلات عن طريق عمليات التحقق المحددة فقط لسجلات هذا السياق أو التي لا تملك سياقًا مطلقًا.

التحقُّقات الصارمة

يمكنك تحديد فيما إذا كان تحقُّق ما صارم (strict) أم لا ورفع الاستثناء ActiveModel::StrictValidationFailed عند فشل عملية التحقق.

class Person < ApplicationRecord
  validates :name, presence: { strict: true }
end
 
Person.new.valid?  # => ActiveModel::StrictValidationFailed: Name can't be blank

يمكنك أيضًا تمرير استثناءات مخصّصة للخيار strict:.

class Person < ApplicationRecord
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
 
Person.new.valid?  # => TokenGenerationException: Token can't be blank

التحققات الشرطية

في بعض الأحيان، قد يكون من المنطقي التحقُّق من سجل ما فقط عند تحقق شرط معيّن. يمكنك إنجاز ذلك عن طريق الخيارين if: و unless:، اللذَين يستقبلان رمزًا، أو كائنًا من النوع Proc، أو مصفوفة. يمكنك استخدام الخيار if: لتحديد متى يجب تنفيذ عملية التحقُّق. وفي حال أردت تحديد متى لا يجب تنفيذ التحقُّق، يمكنك استخدام الخيار unless:.

استخدام رمز مع الخيارين if: و unless:

يمكنك ربط الخيارين if: و unless: مع رمز موافق لاسم التابع المستدعى قبل عملية التحقُّق. هذا الخيار هو الخيار الأكثر استخدامًا.

class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?
 
  def paid_with_card?
    payment_type == "card"
  end
end

استخدام الكائن Proc مع الخيارين if: و unless:

أخيرًا، يمكنك ربط الخيارين if: و unless: مع كائن من النوع Proc يتم مناداته. باستخدام الكائن Proc، يمكنك التحقُّق شرطيًا ضمن سطر من التعليمات بدلًأ من تابع منفصل. هذا الخيار هو مفضّل لمحبّي الأسطر الواحدة.

class Account < ApplicationRecord
  validates :password, confirmation: true,
    unless: Proc.new { |a| a.password.blank? }
end

تجميع التحقُّقات الشرطية

قد يكون من المفيد أحيانًا أن تستخدم مجموعة من التحقُّقات شرطًا واحدًا. يمكن تنفيذ ذلك عن باستخدام with_options.

class User < ApplicationRecord
  with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
    admin.validates :email, presence: true
  end
end

جميع التحقُّقات الواقعة داخل الكتلة التي تخص with_options سيمرّر لها الشرط if: :is_admin?‎ تلقائيًّا.

دمج شروط التحقُّق

وفي الطرف الآخر، عندما تحدد مجموعة من الشروط متى يجب تنفيذ تحقُّق معيّن، يمكن استخدام مصفوفة. أي يمكنك استخدام الخيارين if: و unless: على التحقُّق نفسه.

class Computer < ApplicationRecord
  validates :mouse, presence: true,
                    if: [Proc.new { |c| c.market.retail? }, :desktop?],
                    unless: Proc.new { |c| c.trackpad.present? }
end

يتم تنفيذ هذا التحقُّق عندما تتحقق جميع الشروط في if:، ولا تتحقق أي من الشروط في unless:.

تنفيذ تحققات مخصصة

عند عدم كفاية مساعدات التحقُّق المضمنة، يمكنك كتابة تحقُّقات أو توابع تحقُّق خاصة بك بناءً على حاجتك.

عمليات التحقق المخصصة

عمليات التحقق المخصصة هي أصناف ترث من الصنف الأب ActiveModel::Validator. يجب أن تعرّف هذه الأصناف التابع validate الذي يقبل تمرير السجل المطلوب التحقُّق منه وسيطًا له ثم يجري عملية التحقق عليه. تستدعى علمية التحقق المخصصة باستخدام التابع validates_with.

class MyValidator < ActiveModel::Validator
  def validate(record)
    unless record.name.starts_with? 'X'
      record.errors[:name] << 'Need a name starting with X please!'
    end
  end
end
 
class Person
  include ActiveModel::Validations
  validates_with MyValidator
end

أسهل طريقة لإضافة تحقُّق مخصص للتحقُّق من خاصيات فردية هو عن طريق الصنف المريح ActiveModel::EachValidator. في هذه الحالة، يجب أن يعرِّف صنف التحقُّق التابع validate_each الذي يقبل ثلاث وسائط هي: سجل، وخاصية، وقيمة. تكون هذه الوسائط مرتبطة بالكائن، والخاصية المراد التحقُّق منها، وقيمة الخاصية المُمرَّرة للكائن.

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors[attribute] << (options[:message] || "is not an email")
    end
  end
end
 
class Person < ApplicationRecord
  validates :email, presence: true, email: true
end

كما هو ظاهر في المثال، يمكنك أيضًا دمج التحقُّقات القياسية المُعرَّفة مسبقًا مع تحقُّقات مخصصة.

التوابع المخصصة

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

يمكنك تمرير أكثر من رمز لكل تابع صنف، مسبوقًا بالتحقُّق المستخدم بنفس ترتيب تسجيله.

يتحقق التابع ?valid من خلو مجموعة الأخطاء (errors collection) من الأخطاء، لتتمكّن توابع التحقُّق المخصصة من إضافة الأخطاء إلى المجموعة عند فشل التحقُّق:

class Invoice < ApplicationRecord
  validate :expiration_date_cannot_be_in_the_past,
    :discount_cannot_be_greater_than_total_value
 
  def expiration_date_cannot_be_in_the_past
    if expiration_date.present? && expiration_date < Date.today
      errors.add(:expiration_date, "can't be in the past")
    end
  end
 
  def discount_cannot_be_greater_than_total_value
    if discount > total_value
      errors.add(:discount, "can't be greater than total value")
    end
  end
end

افتراضيًا، يتم تشغيل التحقُّقات المشابهة عند كل استدعاء للتابع ?valid أو عند حفظ الكائن. لكن من الممكن أيضًا التحكم بوقت تنفيذ هذه التحقُّقات المخصصة عن طريق تحديد الخيار on: مع التابع validate، مستخدمًا إما create: أو update:.

class Invoice < ApplicationRecord
  validate :active_customer, on: :create
 
  def active_customer
    errors.add(:customer_id, "is not active") unless customer.active?
  end
end

العمل مع أخطاء التحققات

إضافةً إلى التابعين ?valid و ?invalid الموضّحين أعلاه، يزوّد ريلز بمجموعة من التوابع المستخدمة للعمل مع مجموعة الأخطاء errors والحصول على معلومات حول صحة الكائنات.

إليك قائمة بأكثر التوابع استخدامًا. الرجاء الاطلاع على توثيق ActiveModel::Errors لقائمة بالتوابع المتاحة.

Errors

يعيد كائنًا من النوع ActiveModel::Errors يحوي جميع الأخطاء. كل مفتاح هو اسم الخاصية، وكل قيمة هي مصفوفة برسائل الخطأ المرتبطة بالخاصية.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
 
person = Person.new
person.valid? # => false
person.errors.messages
 # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}
 
person = Person.new(name: "John Doe")
person.valid? # => true
person.errors.messages # => {}

[]errors

يُستخدَم []errors عندما تريد التحقق من رسائل الخطأ لخاصية محددة. يعيد هذا التابع مصفوفة من رسائل الخطأ الموافقة للخاصية المعطاة. في حال لم يكن هنالك أخطاء للحقل المعطى، فيعيد التابع مصفوفةً فارغةً.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
 
person = Person.new(name: "John Doe")
person.valid? # => true
person.errors[:name] # => []
 
person = Person.new(name: "JD")
person.valid? # => false
person.errors[:name] # => ["is too short (minimum is 3 characters)"]
 
person = Person.new
person.valid? # => false
person.errors[:name]
 # => ["can't be blank", "is too short (minimum is 3 characters)"]

errors.add

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

يعيد التابع errors.full_messages (أو مثيله errors.to_a) رسائل الخطأ بنمط سهل القراءة للمستخدم، مع اسم الخاصية السابق لرسالة الخطأ، كما هو ظاهر في المثال التالي.

class Person < ApplicationRecord
  def a_method_used_for_validation_purposes
    errors.add(:name, "cannot contain the characters !@#%*()_-+=")
  end
end
 
person = Person.create(name: "!@#")
 
person.errors[:name]
 # => ["cannot contain the characters !@#%*()_-+="]
 
person.errors.full_messages
 # => ["Name cannot contain the characters !@#%*()_-+="]

مثيل عن التابع errors.add هو استخدام >> لإضافة رسالة إلى المصفوفة errors.messages لخاصية:

class Person < ApplicationRecord
  def a_method_used_for_validation_purposes
    errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
  end
end
 
person = Person.create(name: "!@#")
 
person.errors[:name]
 # => ["cannot contain the characters !@#%*()_-+="]
 
person.errors.to_a
 # => ["Name cannot contain the characters !@#%*()_-+="]

errors.details

يمكنك تحديد نوع التحقُّق لكائن معلومات الخطأ المعاد عن طريق التابع errors.add.

class Person < ApplicationRecord
  def a_method_used_for_validation_purposes
    errors.add(:name, :invalid_characters)
  end
end
 
person = Person.create(name: "!@#")
 
person.errors.details[:name]
# => [{error: :invalid_characters}]

لتحسين كائن معلومات الخطأ بحيث يحوي المحارف غير المسموحة على سبيل المثال، يمكنك تمرير مفاتيح إضافية للتابع errors.add.

class Person < ApplicationRecord
  def a_method_used_for_validation_purposes
    errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=")
  end
end
 
person = Person.create(name: "!@#")
 
person.errors.details[:name]
# => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}]

جميع التحقُّقات المدمجة في ريلز تملأ كائنات معلومات الخطأ بمجموعات متوافقة مع نوع التحقُّق المستخدم.

[errors[:base

يمكنك إضافة رسائل خطأ متعلقة بحالة الكائن ككل، بدلًا من أن تكون متعلقة بخاصية وحيدة. يمكنك استخدام هذا التابع عند الحاجة للقول أن الكائن ككل هو غير صحيح، بغض النظر عن قيم خاصياته. بما أن [errors[:base هي مصفوفة، يمكنك إضافة سلسلة نصية لها وسيتم استخدامها كرسالة خطأ.

class Person < ApplicationRecord
  def a_method_used_for_validation_purposes
    errors[:base] << "This person is invalid because ..."
  end
end

errors.clear

يُستخدَم التابع clear عندما تود بإرادتك أن تزيل جميع رسائل الخطأ من المجموعة errors. بالطبع، استدعاء التابع clear مع كائن غير صحيح لن يجعله صحيحًا: أي ستكون المجموعة errors فارغة، لكن عند استدعاء التابع ?valid في المرة القادمة أو أي تابع يحاول حفظ الكائن في قاعدة البيانات، فسيتم تنفيذ التحقُّق من جديد. وفي حال فشله، ستمتلئ المجموعة errors من جديد.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
 
person = Person.new
person.valid? # => false
person.errors[:name]
 # => ["can't be blank", "is too short (minimum is 3 characters)"]
 
person.errors.clear
person.errors.empty? # => true
 
person.save # => false
 
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]

errors.size

يعيد التابع size عدد رسائل الخطأ للكائن.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
 
person = Person.new
person.valid? # => false
person.errors.size # => 2
 
person = Person.new(name: "Andrea", email: "andrea@example.com")
person.valid? # => true
person.errors.size # => 0

عرض أخطاء التحقق في الواجهات

متى ما أنشأت نموذجًا وأضفت تحقُّقات له، ففي حال تم إنشاء هذا النموذج (model) عن طريق نموذج ويب (web form)، فقد يكون من الضروري إظهار رسالة الخطأ عند فشل التحقُّق.

لأن كل تطبيق يراعي هذه الحالة بطريقة مختلفة، لا يزود ريلز بأي مساعد واجهة للمساعدة بتوليد هذه الرسائل مباشرةً. لكن نظرًا للعدد الكبير من التوابع المزودة من ريلز للتفاعل مع التحقُّقات بشكل عام، من السهل بناء هذه التوابع يدويًّا. علاوةً على ذلك، عند توليد scaffold (هو جزء من النمط البرمجي MVC حيثُ يُمكنك إختيار جزء مُعين من قاعدة البيانات للعمل عليه)، يقوم ريلز بوضع تعليمات ERB في الملف form.html.erb_ المولّد، والذي يظهر القائمة الكاملة بأخطاء هذا النموذج.

بفرض أنه لدينا نموذجًا تم حفظه في متغير نسخة تسمى article@، يبدو الأمر كذلك:

<% 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 %>

من ناحية أخرى، في حال كنت تستخدم مساعدات ريلز لإنشاء نماذج الويب، عند فشل تحقُّق على حقل معين، ستولّد هذه المساعدات عنصر <div> إضافي حول الحقل.

<div class="field_with_errors">
 <input id="article_title" name="article[title]" size="30" type="text" value="">
</div>

يمكنك بناءً على ذلك تعديل شكل هذا العنصر كما تشاء. الشكل الافتراضي المضاف من vdg. مثلًا، يقوم بإنشاء قواعد CSS التالية:

.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}

ممّا يعني أن أي حقل يملك أخطاءً سيتم تلوين حدوده باللون الأحمر بسماكة 2 بكسل.

مصادر