Rails/active record validations

من موسوعة حسوب
< Rails
مراجعة 15:20، 16 يناير 2019 بواسطة جميل-بيلوني (نقاش | مساهمات) (أنشأ الصفحة ب'يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال مي...')
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)
اذهب إلى التنقل اذهب إلى البحث

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

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

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

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

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) هي الطريقة المثلى في معظم الحالات.

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

هناك نوعان من كائنات السجل الفعال: الكائنات التي تتفاعل مع سجل من قاعدة بياناتك، والكائنات التي لا تتفاعل. عند إنشائك لكائن جديد، مثلًا باستخدام التابع new، لن يتم حفظ هذا الكائن في قاعدة البيانات بعد. بعد استدعائك للتابع save على هذا الكائن، سيتم حفظه في جدول قاعدة البيانات. يستخدم السجل الفعال تابع النسخة ?new_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

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

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

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

بعد قيام كائن السجل الفعال بالتحقُّقات، يمكن الوصول لرسائل الخطأ عن طريق تابع النسخة 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 مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات.

مساعدات التحقُّق

يزوّد السجل الفعال بمجموعة معرفة مسبقًا من المساعدات التي يمكنك استخدامها بشكل مباشر في تعريفات الأصناف الخاصة بك. تزوّد هذه المساعدات بقواعد متعارف عليها في التحقُّقات. في كل مرة يفشل فيها تحقُّق ما، سيتم إضافة رسالة خطأ إلى المجموعة 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 لحقل التحقُّق (سنطلع على تحقُّق الوجود لاحقًا في هذا التوثيق):

class Person < ApplicationRecord

 validates :email, confirmation: true

 validates :email_confirmation, presence: true

end

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

class Person < ApplicationRecord

 validates :email, confirmation: { case_sensitive: false }

end

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

مساعد الاقصاء exclusion

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

class Account < ApplicationRecord

 validates :subdomain, exclusion: { in: %w(www us ca jp),

   message: "%{value} is reserved." }

end

يملك مساعد الإقصاء الخيار 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

يملك مساعد الإقصاء الخيار 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 قبل الطول.

مساعد الرقمية numericality

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

وفي حال استخدمت only_integer:، سيتم استخدام التعبير الطبيعي التالي:

/\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، إذا أردت تحقُّق وجود حقل من النوع البولياني، يجب عليك القيام بأحد التحقُّقات التالية:

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: لتحديد حقل أو أكثر مستخدم لتقييد التميز:

class Holiday < ApplicationRecord

 validates :name, uniqueness: { scope: :year,

   message: "should happen once per year" }

end

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

هناك أيضًا خيار case_sensitive: لتحديد فيما إذا كان قيد التميز يراعي حالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي 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 صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق أصناف التحقُّق.

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

كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات if: و unless: و on:. إذا مرّرت خيارات إضافية، سيتم تمريرها إلى صنف التحقُّق ضمن الوسيط 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

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

إذا كان صنف التحقُّق الخاص بك معقدًا لدرجة حاجته لمتغيّرات المثل، يمكنك استخدام كائنات Ruby البسيطة بدلًا من ذلك:

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 =~ /\Alower:/

 end

end

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

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

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

الخيار 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

لقراءة كل الخيارات حول وسيط الرسالة اطّلع على خيار الرسالة.

الخيار 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 عند فشل التحقُّق. عند عدم تحديد هذا الخيار، يقوم السجل الفعال بإرسال رسالة الخطأ الافتراضية من أجل كل مساعد تحقُّق. يقبل الخيار message: إما String أو Proc.

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

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

class Person < ApplicationRecord

 # رسالة ثابتة

 validates :name, presence: { message: "must be given please" }

 # رسالة مع قيمة ديناميكية. %{value} يتم استبدالها

 # بالقيمة الحقيقية للحقل. %{attribute} و %{model} أيضًا متاحة.

 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

 # it will be possible to update email with a duplicated value

 validates :email, uniqueness: true, on: :create

 # it will be possible to create the record with a non-numerical age

 validates :age, numericality: true, on: :update

 # the default (validates on both create and 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 قبل الحفظ. عند التشغيل بشكل ظاهري، يتم تحقُّق السجلات عن طريق المؤكدات المحددة فقط للسجلات لهذا السياق أو التي لا تملك سياقًا مطلقًا.

التحقُّقات الدقيقة

يمكنك تحديد فيما إذا كان تحقُّق ما دقيق أم لا ورفع استثناء 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، أو مصفوفة Array. يمكنك استخدام الخيار 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 من خلو مجموعة الأخطاء من الأخطاء، لتتمكّن توابع التحقُّق المخصصة من إضافة الأخطاء إلى المجموعة عند فشل التحقُّق:

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 الموضّحة أعلاه، يزوّد Rails بمجموعة من التوابع المستخدمة للعمل مع مجموعة الأخطاء 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: "!@#%*()_-+="}]

جميع مؤكدات Rails المضمنة تملأ كائنات معلومات الخطأ بمجموعات متوافقة مع نوع المؤكد المستخدم.

[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

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

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

لأن كل تطبيق يراعي هذه الحالة بطريقة مختلفة، لا يزود Rails بأي مساعد واجهة للمساعدة بتوليد هذه الرسائل مباشرةً. لكن نظرًا للعدد الكبير من التوابع المزودة من Rails للتفاعل مع التحقُّقات بشكل عام، من السهل بناء هذه التوابع بيدك. علاوةً على ذلك، عند توليد مجموعة، يقوم Rails بوضع تعليمات 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 %>

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

<div class="field_with_errors">

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

</div>

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

.field_with_errors {

 padding: 2px;

 background-color: red;

 display: table;

}

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