(मैंने यह पोस्ट कुछ महीने पहले अपनी सूची को भेजी थी। अगर आपको यह पसंद है, और आप इस तरह की और पोस्ट पढ़ना चाहते हैं, तो आपको साइन अप करना चाहिए!)
मेरे एक ऐप के साथ दूसरे दिन एक अजीब स्थिति आई।
मान लें कि आपके पास एक Article मॉडल है, और ये लेख पहले ड्राफ्ट के रूप में बनाए जाते हैं। ये ड्राफ्ट हल्के होने चाहिए। आप उन्हें तुरंत बनाने और संपादित करने में सक्षम होना चाहते हैं, बिना बॉडी की आवश्यकता के, या यहां तक कि शीर्षक के भी।
लेकिन जब आप इन लेखों को प्रकाशित करते हैं, तो आपको कुछ अतिरिक्त वैलिडेशन की आवश्यकता होती है। उनके पास एक शीर्षक, एक बॉडी, और वह सारा सामान होना चाहिए।
आप जांच करने के लिए if स्टेटमेंट्स का एक गुच्छा लिख सकते हैं। लेकिन आपके पास रेल्स से मिलने वाली सभी अच्छी फॉर्म हैंडलिंग नहीं होगी। और मैंने पाया है कि उन चीजों के लिए रेल्स के साथ जाना जो रेल्स अच्छी तरह से संभालता है, आपको बाद में बहुत सारे सिरदर्द से बचाएगा। क्या इसके लिए अभी भी वैलिडेशन का उपयोग करने का कोई तरीका है?
if और unless?
वैलिडेशन्स :if और :unless पैरामीटर्स का समर्थन करते हैं, लेकिन यह हमेशा इतना साफ नहीं होता:
class Article < ActiveRecord::Base
# validate in draft mode
validates_presence_of :author_id
# validate only when published
validates_presence_of :body, unless: lambda { |o| o.draft? }
...
end
इसके अलावा, यह वह तरीका नहीं है जिस तरह से मैं एक लेख प्रकाशित करना चाहता हूं। मैं एक लेख प्रकाशित करने के बारे में एक क्रिया के रूप में सोचना चाहता हूं, न कि एक स्थिति जिसमें लेख है, इसलिए एक एट्रिब्यूट सेट करना और उसकी जांच करना सही उत्तर नहीं है।
एक कम-ज्ञात, हल्के ढंग से प्रलेखित रेल्स फीचर
यह समस्या रेल्स के वैलिडेशन कॉन्टेक्स्ट्स के लिए एकदम सही है। मैंने DHH द्वारा लिखे एक गिस्ट से कस्टम वैलिडेशन कॉन्टेक्स्ट्स के बारे में पहली बार सीखा। लेकिन यह ढूंढना मुश्किल था कि ये क्या थे, और उनका उपयोग कैसे करें, इसका एक अच्छा विवरण।
कस्टम कॉन्टेक्स्ट्स के लिए डॉक्यूमेंटेशन काफी पतला है। इसका एकमात्र उल्लेख जो मुझे API डॉक्स में मिला वह यहां था। और इन वैलिडेशन्स के लिए API डॉक्स कस्टम कॉन्टेक्स्ट्स का बिल्कुल उल्लेख नहीं करते! यह इसे विशेष रूप से कठिन बनाता है - यदि आप नहीं जानते कि किसी चीज़ को क्या कहा जाता है, तो आप उस पर अधिक डॉक्यूमेंटेशन कैसे ढूंढते हैं?
मैंने अंततः यहां एक अच्छा अवलोकन पाया: https://blog.arkency.com/2014/04/mastering-rails-validations-contexts/। यह पढ़ने लायक है। आइए देखें कि यह हमारे उदाहरण के लिए कैसा दिखता है:
class Article < ActiveRecord::Base
# validate in draft mode
validates_presence_of :author_id
# validate only when published
validates_presence_of :body, on: :publish
...
end
class ArticleController < ApplicationController
respond_to :html
def create
@article = Article.create(article_params)
respond_with @article
end
def publish
@article = Article.find(params[:id])
@article.attributes = article_params
@article.save(context: :publish)
respond_with @article
end
private
def article_params
params. require(:article). permit(:body, :title, :author_id)
end
end
बहुत बुरा नहीं! एकमात्र अजीब बात पब्लिश मेथड में update के बजाय save का उपयोग करना है। ऐसा इसलिए होता है क्योंकि update पैरामीटर के रूप में वैलिडेशन कॉन्टेक्स्ट नहीं ले सकता। (शायद यह पुल रिक्वेस्ट के लिए एक अवसर है?)
सेविंग से परे
कस्टम वैलिडेशन कॉन्टेक्स्ट्स केवल अलग-अलग तरीकों से रिकॉर्ड सेव करने से अधिक के लिए उपयोगी हैं। कॉन्टेक्स्ट valid? के साथ काम करते हैं:
@article.valid?(:publish)
इसलिए आप वैलिडेशन कॉन्टेक्स्ट्स का उपयोग कहीं भी कर सकते हैं जहां आप यह प्रश्न पूछना चाहते हैं: क्या इस ऑब्जेक्ट में यह संपत्ति है? और यदि नहीं, तो क्यों नहीं?
DHH एक मेथड बनाते हैं जो -able? में समाप्त होता है जो valid?(:context) को डेलीगेट करता है। मुझे वास्तव में उसका लुक पसंद है:
class Article
validate :has_been_published, on: :view
validate :is_not_spam, on: :view
def viewable?
valid? :view
end
private
def has_been_published
if published_at.future?
errors.add(:published_at, "is in the future")
end
end
def is_not_spam
if is_spam?(body)
errors.add(:body, "has been detected as spam")
end
end
end
इस तरह, जब आप इसे जांचते हैं, तो आपको केवल true या false से अधिक जानकारी मिलती है:
unless @article.viewable?
# @article.errors अब भरा हुआ है
end
क्या लेख देखने योग्य है? खैर, क्यों नहीं?
बस पर्याप्त हल्का
कुछ अन्य तरीके हैं जिनसे आप इस तरह का वैलिडेशन व्यवहार प्राप्त कर सकते हैं। आप अपने स्वयं के वैलिडेशन्स के साथ कस्टम ActiveModel सर्विस ऑब्जेक्ट्स बना सकते हैं। आप अपने वैलिडेशन्स पर :if या :unless का उपयोग कर सकते हैं। लेकिन रेल्स में बहुत सी चीजों की तरह, कस्टम वैलिडेशन कॉन्टेक्स्ट्स पठनीयता और लचीलेपन के बीच एक अच्छा समझौता हैं।