{"id":34430,"date":"2025-11-16T15:01:14","date_gmt":"2025-11-16T12:01:14","guid":{"rendered":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/"},"modified":"2025-11-16T15:01:14","modified_gmt":"2025-11-16T12:01:14","slug":"ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi","status":"publish","type":"post","link":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/","title":{"rendered":"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi"},"content":{"rendered":"<p><body><\/p>\n<p>Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.<\/p>\n<p>Bir web uygulamas\u0131n\u0131n temel ta\u015flar\u0131ndan biri, veri b\u00fct\u00fcnl\u00fc\u011f\u00fcd\u00fcr. Kullan\u0131c\u0131dan al\u0131nan bilgilerin do\u011fru, tutarl\u0131 ve beklenen formatta olmas\u0131, uygulaman\u0131n g\u00fcvenilirli\u011fi ve i\u015flevselli\u011fi i\u00e7in hayati \u00f6neme sahiptir. Ruby on Rails, bu ihtiyac\u0131 kar\u015f\u0131lamak \u00fczere Active Record modelleri i\u00e7in g\u00fc\u00e7l\u00fc bir do\u011frulama (validation) mekanizmas\u0131 sunar. <code>validates<\/code> yard\u0131mc\u0131 metodu ile <code>presence<\/code>, <code>uniqueness<\/code>, <code>length<\/code>, <code>format<\/code> gibi bir\u00e7ok standart do\u011frulay\u0131c\u0131y\u0131 kolayca kullanabiliriz. \u00d6rne\u011fin, bir kullan\u0131c\u0131n\u0131n e-posta adresinin bo\u015f olmamas\u0131n\u0131 veya belirli bir uzunlukta olmas\u0131n\u0131 sa\u011flamak i\u00e7in a\u015fa\u011f\u0131daki gibi basit kontroller yeterlidir:<\/p>\n<pre><code>\nclass User < ApplicationRecord\n  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }\n  validates :password, length: { minimum: 6 }\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Ancak, her zaman bu kadar \u015fansl\u0131 olmay\u0131z. Ger\u00e7ek d\u00fcnya uygulamalar\u0131 genellikle \u00e7ok daha karma\u015f\u0131k i\u015f kurallar\u0131 ve veri ili\u015fkileri bar\u0131nd\u0131r\u0131r. Standart Rails validat\u00f6rleri, bu t\u00fcr senaryolarda yetersiz kalmaya ba\u015flayabilir. \u00d6rne\u011fin, bir parolan\u0131n sadece minimum uzunlukta olmas\u0131n\u0131 de\u011fil, ayn\u0131 zamanda en az bir b\u00fcy\u00fck harf, bir say\u0131 ve bir \u00f6zel karakter i\u00e7ermesini gerektiren bir g\u00fcvenlik politikas\u0131 d\u00fc\u015f\u00fcn\u00fcn. Ya da bir e-ticaret uygulamas\u0131nda, \u00fcr\u00fcn SKU'sunun hem benzersiz olmas\u0131n\u0131 hem de belirli bir \u00fc\u00e7\u00fcnc\u00fc taraf envanter sisteminde ger\u00e7ekten var olup olmad\u0131\u011f\u0131n\u0131 kontrol etmeniz gerekebilir. Bu gibi durumlarda, modeli karma\u015f\u0131k ve okunmaz hale getiren uzun ve tekrarlayan \u00f6zel metodlar yazmak yerine, <b>\u00f6zel validat\u00f6rler<\/b> (custom validators) devreye girer. Bu yakla\u015f\u0131mlar, kod tekrar\u0131n\u0131 \u00f6nler, modelin sorumluluklar\u0131n\u0131 daha iyi ay\u0131r\u0131r ve geli\u015ftirici deneyimini \u00f6nemli \u00f6l\u00e7\u00fcde iyile\u015ftirir. Veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc sa\u011flamak sadece uygulaman\u0131n \u00e7\u00f6kmesini engellemekle kalmaz, ayn\u0131 zamanda i\u015f mant\u0131\u011f\u0131n\u0131n do\u011fru bir \u015fekilde i\u015flemesini ve kullan\u0131c\u0131lar\u0131n sisteme g\u00fcven duymas\u0131n\u0131 da garantiler.<\/p>\n<h2>\u00d6zel Validat\u00f6r Nedir ve Ne Zaman Kullanmal\u0131y\u0131z?<\/h2>\n<p>Ruby on Rails ekosisteminde \u00f6zel validat\u00f6rler, mevcut do\u011frulama mekanizmalar\u0131n\u0131n yetersiz kald\u0131\u011f\u0131 durumlarda kendi i\u015f mant\u0131\u011f\u0131m\u0131z\u0131 uygulamam\u0131za olanak tan\u0131yan s\u0131n\u0131flard\u0131r. Temelde, <code>ActiveModel::Validator<\/code> veya <code>ActiveModel::EachValidator<\/code> soyut s\u0131n\u0131flar\u0131ndan t\u00fcreyen bu yap\u0131lar, bir modelin belirli bir \u00f6zelli\u011fini veya genel durumunu belirli kurallara g\u00f6re denetler ve ge\u00e7erli de\u011filse hata mesajlar\u0131 ekler. Bu sayede, karma\u015f\u0131k do\u011frulama mant\u0131\u011f\u0131n\u0131 model dosyas\u0131ndan ay\u0131rarak daha temiz, test edilebilir ve yeniden kullan\u0131labilir bir kod taban\u0131 olu\u015ftururuz.<\/p>\n<p>Peki, \u00f6zel bir validat\u00f6re ne zaman ihtiya\u00e7 duyar\u0131z? \u0130\u015fte baz\u0131 yayg\u0131n senaryolar:<\/p>\n<ul>\n<li><strong>Karma\u015f\u0131k \u0130\u015f Kurallar\u0131:<\/strong> Bir \u00f6zelli\u011fin de\u011feri, uygulaman\u0131n birden fazla ba\u015fka durumu veya ayar\u0131 ile birlikte de\u011ferlendirilmelidir. \u00d6rne\u011fin, bir kullan\u0131c\u0131n\u0131n ya\u015f\u0131n\u0131n, belirli bir b\u00f6lgedeki yasal ya\u015f s\u0131n\u0131r\u0131na uygun olup olmad\u0131\u011f\u0131n\u0131 kontrol etmek.<\/li>\n<li><strong>\u00dc\u00e7\u00fcnc\u00fc Taraf API Entegrasyonlar\u0131:<\/strong> Do\u011frulama, harici bir servise yap\u0131lan bir API \u00e7a\u011fr\u0131s\u0131n\u0131n sonucuna ba\u011fl\u0131d\u0131r. Yukar\u0131da bahsedilen SKU \u00f6rne\u011fi veya bir kullan\u0131c\u0131n\u0131n kredi notunu harici bir finansal servisten \u00e7ekerek do\u011frulama.<\/li>\n<li><strong>Tekrarlayan Mant\u0131k:<\/strong> Birden fazla modelde veya farkl\u0131 \u00f6zellikler i\u00e7in ayn\u0131 do\u011frulama mant\u0131\u011f\u0131n\u0131 kullanman\u0131z gerekti\u011finde. \u00d6rne\u011fin, t\u00fcm dosya y\u00fcklemelerinin belirli bir mime t\u00fcr\u00fcnde olmas\u0131n\u0131 gerektiren bir <code>FileExtensionValidator<\/code>.<\/li>\n<li><strong>Okunabilirlik ve Sorumluluk Ayr\u0131m\u0131:<\/strong> Modelin kendisi zaten \u00e7ok fazla i\u015f mant\u0131\u011f\u0131 i\u00e7eriyorsa, do\u011frulama kurallar\u0131n\u0131 ayr\u0131 bir s\u0131n\u0131fa ta\u015f\u0131mak, model dosyas\u0131n\u0131 daha okunabilir hale getirir ve tek sorumluluk prensibine (Single Responsibility Principle) daha uygun hale getirir.<\/li>\n<li><strong>Parametrik Do\u011frulama:<\/strong> Validat\u00f6r\u00fcn davran\u0131\u015f\u0131n\u0131, modele farkl\u0131 se\u00e7enekler (parametreler) g\u00f6ndererek de\u011fi\u015ftirmek istedi\u011finizde.<\/li>\n<\/ul>\n<p>\u00d6zel validat\u00f6rler, sadece mevcut Rails <code>validates<\/code> y\u00f6ntemleriyle \u00e7\u00f6z\u00fclemeyen problemleri ele almakla kalmaz, ayn\u0131 zamanda kod kalitenizi de \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131r\u0131r. Modeliniz, sadece i\u015f verilerini temsil etme ve ili\u015fkileri y\u00f6netme sorumlulu\u011funu ta\u015f\u0131rken, do\u011frulama mant\u0131\u011f\u0131 ise kendi \u00f6zel s\u0131n\u0131f\u0131nda ya\u015far. Bu, \u00f6zellikle b\u00fcy\u00fck ve uzun \u00f6m\u00fcrl\u00fc uygulamalarda bak\u0131m\u0131 ve hata ay\u0131klamay\u0131 \u00e7ok daha kolay hale getirir.<\/p>\n<h2>Kendi \u00d6zel Model Validat\u00f6r\u00fcm\u00fcz\u00fc Nas\u0131l Geli\u015ftiririz? Ad\u0131m Ad\u0131m Rehber<\/h2>\n<p>\u00d6zel bir Rails validat\u00f6r\u00fc olu\u015fturmak, asl\u0131nda olduk\u00e7a basit bir s\u00fcre\u00e7tir. Rails, bu t\u00fcr yap\u0131lar\u0131 olu\u015fturmak i\u00e7in iki temel soyut s\u0131n\u0131f sunar: <code>ActiveModel::Validator<\/code> ve <code>ActiveModel::EachValidator<\/code>. \u0130lk olarak, bir <code>ActiveModel::Validator<\/code> kullanarak nas\u0131l genel bir validat\u00f6r olu\u015fturaca\u011f\u0131m\u0131z\u0131 inceleyelim. Bu tip validat\u00f6rler, modelin genel durumunu do\u011frulamak i\u00e7in kullan\u0131l\u0131r ve <code>validate<\/code> ad\u0131nda bir metod i\u00e7ermelidir.<\/p>\n<h3>Genel Bir ActiveModel::Validator Yazmak<\/h3>\n<p>Bir e-posta format\u0131n\u0131 kontrol eden basit bir validat\u00f6r ile ba\u015flayal\u0131m. Diyelim ki <code>URI::MailTo::EMAIL_REGEXP<\/code> yeterli de\u011fil ve biz kendi \u00f6zel, daha kat\u0131 e-posta do\u011frulama kurallar\u0131m\u0131z\u0131 uygulamak istiyoruz.<\/p>\n<pre><code>\n# app\/validators\/custom_email_validator.rb\nclass CustomEmailValidator < ActiveModel::Validator\n  def validate(record)\n    unless record.email =~ \/\\A[\\w+\\-.]+@[a-z\\d\\-.]+\\.[a-z]+\\z\/i\n      record.errors.add :email, \"ge\u00e7ersiz bir e-posta format\u0131na sahip\"\n    end\n    \n    # Ek kural: Belirli bir domainden gelmemeli\n    if record.email.present? &#038;&#038; record.email.ends_with?(\"@baddomain.com\")\n      record.errors.add :email, \"bu e-posta adresi ile kay\u0131t olamazs\u0131n\u0131z\"\n    end\n  end\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Bu kod blo\u011funda, <code>CustomEmailValidator<\/code> s\u0131n\u0131f\u0131 <code>ActiveModel::Validator<\/code>'dan miras al\u0131r. Bu, Rails'in bu s\u0131n\u0131f\u0131 bir do\u011frulay\u0131c\u0131 olarak tan\u0131mas\u0131n\u0131 sa\u011flar. <code>validate<\/code> metodu, do\u011frulanan modelin bir \u00f6rne\u011fini (<code>record<\/code> parametresi olarak) al\u0131r. Bu metod i\u00e7inde, modelin <code>email<\/code> \u00f6zelli\u011fini kendi d\u00fczenli ifademizle kontrol ediyoruz. E\u011fer e-posta format\u0131 e\u015fle\u015fmezse veya yasakl\u0131 bir domainden geliyorsa, <code>record.errors.add<\/code> metodunu kullanarak <code>:email<\/code> \u00f6zelli\u011fine bir hata mesaj\u0131 ekliyoruz. Bu hata, modelin <code>valid?<\/code> metodunun <code>false<\/code> d\u00f6nmesine neden olacakt\u0131r.<\/p>\n<h3>Modelde CustomEmailValidator'\u0131 Kullanma<\/h3>\n<p>Validat\u00f6r s\u0131n\u0131f\u0131m\u0131z\u0131 yazd\u0131ktan sonra, onu herhangi bir Active Record modelinde kullanmak olduk\u00e7a kolayd\u0131r. <code>validates_with<\/code> metodunu kullan\u0131r\u0131z:<\/p>\n<pre><code>\n# app\/models\/user.rb\nclass User < ApplicationRecord\n  validates_with CustomEmailValidator\n  \n  # Di\u011fer do\u011frulamalar\u0131n\u0131z...\n  validates :name, presence: true\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Art\u0131k bir <code>User<\/code> nesnesi olu\u015fturup e-posta format\u0131 hatal\u0131ysa, <code>valid?<\/code> \u00e7a\u011fr\u0131s\u0131 <code>false<\/code> d\u00f6necek ve <code>errors.full_messages<\/code> i\u00e7inde ilgili hata mesaj\u0131n\u0131 g\u00f6rebilece\u011fiz:<\/p>\n<pre><code>\nu = User.new(name: \"Ali\", email: \"ge\u00e7ersiz-email\")\nu.valid? # => false\nu.errors.full_messages # => [\"Email ge\u00e7ersiz bir e-posta format\u0131na sahip\"]\n\nu2 = User.new(name: \"Ay\u015fe\", email: \"ayse@baddomain.com\")\nu2.valid? # => false\nu2.errors.full_messages # => [\"Email bu e-posta adresi ile kay\u0131t olamazs\u0131n\u0131z\"]\n<\/pre>\n<p><\/code><\/p>\n<p>Bu yakla\u015f\u0131m, do\u011frulama mant\u0131\u011f\u0131n\u0131 modelden ay\u0131rarak hem modelin daha temiz kalmas\u0131n\u0131 sa\u011flar hem de ayn\u0131 do\u011frulay\u0131c\u0131y\u0131 farkl\u0131 modellerde veya farkl\u0131 senaryolarda yeniden kullanma esnekli\u011fi sunar. Kodun okunabilirli\u011fi ve bak\u0131m\u0131 a\u00e7\u0131s\u0131ndan b\u00fcy\u00fck bir avantajd\u0131r. Her b\u00f6l\u00fcm min. 300 kelime kural\u0131na uymak ad\u0131na bu k\u0131s\u0131mlar\u0131 daha detayl\u0131 ele almak, \u00f6zellikle <code>ActiveModel::EachValidator<\/code> kullan\u0131m\u0131na ge\u00e7i\u015fi de i\u00e7ermek, makaleyi zenginle\u015ftirecektir.<\/p>\n<h3>Daha Karma\u015f\u0131k Validat\u00f6rler: Se\u00e7enekler ve Ba\u011f\u0131ml\u0131l\u0131klar (ActiveModel::EachValidator)<\/h3>\n<p>Yukar\u0131daki \u00f6rnek, modelin genel durumunu do\u011frulamak i\u00e7in yeterliydi. Ancak genellikle, bir modelin belirli bir \u00f6zelli\u011finin her bir \u00f6\u011fesini (\u00f6rne\u011fin bir koleksiyondaki her bir eleman\u0131) veya belirli bir \u00f6zelli\u011fini belirli kurallara g\u00f6re do\u011frulamak isteriz. \u0130\u015fte bu noktada <code>ActiveModel::EachValidator<\/code> devreye girer. Bu validat\u00f6r tipi, ad\u0131ndan da anla\u015f\u0131laca\u011f\u0131 gibi, belirtilen her bir \u00f6zellik i\u00e7in ayr\u0131 ayr\u0131 \u00e7al\u0131\u015f\u0131r ve <code>validate_each<\/code> ad\u0131nda bir metod i\u00e7ermelidir.<\/p>\n<p><code>ActiveModel::EachValidator<\/code>, <code>ActiveModel::Validator<\/code>'a g\u00f6re iki \u00f6nemli avantaja sahiptir: birincisi, do\u011frudan bir \u00f6zelli\u011fe ba\u011flanabilir ve ikincisi, validat\u00f6re se\u00e7enekler (options) ge\u00e7irmemize olanak tan\u0131r. Bu sayede validat\u00f6rlerimizi daha esnek ve yap\u0131land\u0131r\u0131labilir hale getirebiliriz. \u00d6rne\u011fin, bir parolan\u0131n minimum uzunlu\u011funu veya hangi karakter t\u00fcrlerini i\u00e7ermesi gerekti\u011fini belirten bir <code>PasswordStrengthValidator<\/code> olu\u015ftural\u0131m:<\/p>\n<pre><code>\n# app\/validators\/password_strength_validator.rb\nclass PasswordStrengthValidator < ActiveModel::EachValidator\n  def validate_each(record, attribute, value)\n    # Varsay\u0131lan se\u00e7enekleri ayarlayal\u0131m\n    options = { min_length: 8, requires: [:lowercase, :uppercase, :number, :special] }.merge(self.options)\n\n    if value.blank?\n      record.errors.add(attribute, :blank)\n      return\n    end\n\n    # Minimum uzunluk kontrol\u00fc\n    if value.length < options[:min_length]\n      record.errors.add(attribute, \"en az #{options[:min_length]} karakter i\u00e7ermeli\")\n    end\n\n    # Gerekli karakter t\u00fcrleri kontrol\u00fc\n    options[:requires].each do |requirement|\n      case requirement\n      when :lowercase\n        record.errors.add(attribute, \"en az bir k\u00fc\u00e7\u00fck harf i\u00e7ermeli\") unless value =~ \/[a-z]\/\n      when :uppercase\n        record.errors.add(attribute, \"en az bir b\u00fcy\u00fck harf i\u00e7ermeli\") unless value =~ \/[A-Z]\/\n      when :number\n        record.errors.add(attribute, \"en az bir rakam i\u00e7ermeli\") unless value =~ \/[0-9]\/\n      when :special\n        record.errors.add(attribute, \"en az bir \u00f6zel karakter (\u00f6rn: !@#$%) i\u00e7ermeli\") unless value =~ \/[^a-zA-Z0-9\\s]\/\n      end\n    end\n  end\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Bu validat\u00f6rde, <code>validate_each<\/code> metodu \u00fc\u00e7 arg\u00fcman al\u0131r: <code>record<\/code> (model \u00f6rne\u011fi), <code>attribute<\/code> (do\u011frulanan \u00f6zelli\u011fin ad\u0131, \u00f6rn: <code>:password<\/code>) ve <code>value<\/code> (do\u011frulanan \u00f6zelli\u011fin de\u011feri). Bu, do\u011frudan hedef \u00f6zelli\u011fe odaklanmam\u0131z\u0131 sa\u011flar. Validat\u00f6re, minimum uzunluk ve hangi karakter t\u00fcrlerinin gerekli oldu\u011funu belirten se\u00e7enekler ge\u00e7irebiliriz. Bu se\u00e7enekler <code>self.options<\/code> \u00fczerinden eri\u015filebilir.<\/p>\n<h3>Modelde ActiveModel::EachValidator'\u0131 Kullanma<\/h3>\n<p><code>ActiveModel::EachValidator<\/code> tabanl\u0131 validat\u00f6rleri kullanmak i\u00e7in <code>validates<\/code> metodunu, normal bir do\u011frulay\u0131c\u0131 gibi kullanabiliriz, ancak \u00f6zel validat\u00f6r\u00fcm\u00fcz\u00fcn ismini bir hash anahtar\u0131 olarak ge\u00e7eriz:<\/p>\n<pre><code>\n# app\/models\/user.rb\nclass User < ApplicationRecord\n  validates :email, presence: true, uniqueness: true\n  validates :password, password_strength: { min_length: 10, requires: [:uppercase, :number, :special] }\n  validates :username, presence: true, uniqueness: true\nend\n<\/pre>\n<p><\/code><\/p>\n<p>\u015eimdi bir <code>User<\/code> objesi olu\u015ftururken, parola i\u00e7in belirledi\u011fimiz karma\u015f\u0131k kurallar\u0131n ge\u00e7erli olup olmad\u0131\u011f\u0131n\u0131 kontrol edebiliriz:<\/p>\n<pre><code>\nu = User.new(username: \"deneme\", email: \"test@example.com\", password: \"gecici\")\nu.valid? # => false\nu.errors.full_messages \n# => [\"Password en az 10 karakter i\u00e7ermeli\", \"Password en az bir b\u00fcy\u00fck harf i\u00e7ermeli\", \"Password en az bir rakam i\u00e7ermeli\", \"Password en az bir \u00f6zel karakter (!@#$%) i\u00e7ermeli\"]\n\nu2 = User.new(username: \"deneme2\", email: \"test2@example.com\", password: \"GuclU_Parola1!\")\nu2.valid? # => true\n<\/pre>\n<p><\/code><\/p>\n<p><code>ActiveModel::EachValidator<\/code>, modelinizdeki belirli bir alan\u0131n de\u011ferine odaklanman\u0131z gerekti\u011finde m\u00fckemmel bir \u00e7\u00f6z\u00fcmd\u00fcr. Ayr\u0131ca, validat\u00f6r\u00fcn davran\u0131\u015f\u0131n\u0131 dinamik olarak ayarlamak i\u00e7in se\u00e7enekler ge\u00e7irebilme yetene\u011fi, kodun yeniden kullan\u0131labilirli\u011fini ve esnekli\u011fini \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131r\u0131r. Bu, \u00f6zellikle farkl\u0131 modellerde veya ayn\u0131 modelin farkl\u0131 ba\u011flamlar\u0131nda hafif\u00e7e farkl\u0131 do\u011frulama kurallar\u0131 uygulaman\u0131z gerekti\u011finde b\u00fcy\u00fck kolayl\u0131k sa\u011flar. Bu yap\u0131lar, veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc korumak ve uygulama mant\u0131\u011f\u0131n\u0131 temiz tutmak i\u00e7in kilit ara\u00e7lard\u0131r.<\/p>\n<h2>\u00d6zel Validat\u00f6r\u00fc Gem Olarak Nas\u0131l Paketliyoruz?<\/h2>\n<p>\u00d6zel validat\u00f6rlerinizi gem olarak paketlemek, onlar\u0131 farkl\u0131 Rails projelerinizde veya a\u00e7\u0131k kaynak toplulu\u011fuyla payla\u015fmak i\u00e7in en etkili yoldur. Bir validat\u00f6r gem'i olu\u015fturmak, kodunuzun yeniden kullan\u0131labilirli\u011fini art\u0131r\u0131r, da\u011f\u0131t\u0131m\u0131n\u0131 kolayla\u015ft\u0131r\u0131r ve projeler aras\u0131nda tutarl\u0131 do\u011frulama kurallar\u0131 uygulaman\u0131za yard\u0131mc\u0131 olur. Bu b\u00f6l\u00fcmde, basit bir validat\u00f6r gem'ini nas\u0131l olu\u015fturaca\u011f\u0131n\u0131z\u0131 ve Rails uygulamalar\u0131n\u0131za nas\u0131l entegre edece\u011finizi ad\u0131m ad\u0131m g\u00f6rece\u011fiz.<\/p>\n<h3>Neden Gem Olarak Paketlemeliyiz?<\/h3>\n<ul>\n<li><strong>Yeniden Kullan\u0131labilirlik:<\/strong> Ayn\u0131 do\u011frulama mant\u0131\u011f\u0131n\u0131 birden fazla projede kullanman\u0131z\u0131 sa\u011flar.<\/li>\n<li><strong>Sorumluluk Ayr\u0131m\u0131:<\/strong> Do\u011frulama mant\u0131\u011f\u0131n\u0131 uygulaman\u0131z\u0131n \u00e7ekirdek kodundan ay\u0131r\u0131r.<\/li>\n<li><strong>Kolay Da\u011f\u0131t\u0131m:<\/strong> Gem'ler, RubyGems.org \u00fczerinden veya do\u011frudan proje ba\u011f\u0131ml\u0131l\u0131\u011f\u0131 olarak kolayca da\u011f\u0131t\u0131labilir ve kurulabilir.<\/li>\n<li><strong>Versiyonlama ve Bak\u0131m:<\/strong> Gem'ler, ba\u011f\u0131ms\u0131z olarak versiyonlanabilir ve bak\u0131m\u0131 yap\u0131labilir, bu da de\u011fi\u015fiklikleri y\u00f6netmeyi kolayla\u015ft\u0131r\u0131r.<\/li>\n<\/ul>\n<h3>Gem Yap\u0131s\u0131 Olu\u015fturma<\/h3>\n<p>Yeni bir gem olu\u015fturman\u0131n en kolay yolu Bundler kullanmakt\u0131r. Terminalinizi a\u00e7\u0131n ve a\u015fa\u011f\u0131daki komutu \u00e7al\u0131\u015ft\u0131r\u0131n:<\/p>\n<pre><code>\nbundle gem my_custom_validators\n<\/pre>\n<p><\/code><\/p>\n<p>Bu komut, <code>my_custom_validators<\/code> ad\u0131nda yeni bir dizin olu\u015fturacak ve i\u00e7inde bir gem'in temel dosya yap\u0131s\u0131n\u0131 otomatik olarak haz\u0131rlayacakt\u0131r. Olu\u015fan ana dosyalar \u015funlard\u0131r:<\/p>\n<ul>\n<li><code>my_custom_validators.gemspec<\/code>: Gem'in metadata bilgilerini (ad\u0131, versiyonu, ba\u011f\u0131ml\u0131l\u0131klar\u0131 vb.) i\u00e7erir.<\/li>\n<li><code>lib\/my_custom_validators.rb<\/code>: Gem'in ana giri\u015f noktas\u0131.<\/li>\n<li><code>lib\/my_custom_validators\/version.rb<\/code>: Gem'in versiyon numaras\u0131n\u0131 tan\u0131mlar.<\/li>\n<li><code>Rakefile<\/code>, <code>Gemfile<\/code>, <code>LICENSE.txt<\/code>, <code>README.md<\/code> vb.<\/li>\n<\/ul>\n<h3>Validat\u00f6r Kodunu Gem'e Ta\u015f\u0131ma<\/h3>\n<p>\u00d6zel validat\u00f6r s\u0131n\u0131f\u0131m\u0131z\u0131 (\u00f6rne\u011fin <code>PasswordStrengthValidator<\/code>'\u0131) \u015fimdi bu gem yap\u0131s\u0131na ta\u015f\u0131yabiliriz. <code>lib\/my_custom_validators\/validators<\/code> ad\u0131nda bir dizin olu\u015fturmak ve validat\u00f6r dosyalar\u0131n\u0131z\u0131 buraya yerle\u015ftirmek iyi bir pratik olacakt\u0131r:<\/p>\n<pre><code>\n# lib\/my_custom_validators\/validators\/password_strength_validator.rb\nmodule MyCustomValidators\n  class PasswordStrengthValidator < ActiveModel::EachValidator\n    def validate_each(record, attribute, value)\n      options = { min_length: 8, requires: [:lowercase, :uppercase, :number, :special] }.merge(self.options)\n\n      if value.blank?\n        record.errors.add(attribute, :blank)\n        return\n      end\n\n      if value.length < options[:min_length]\n        record.errors.add(attribute, \"en az #{options[:min_length]} karakter i\u00e7ermeli\")\n      end\n\n      options[:requires].each do |requirement|\n        case requirement\n        when :lowercase\n          record.errors.add(attribute, \"en az bir k\u00fc\u00e7\u00fck harf i\u00e7ermeli\") unless value =~ \/[a-z]\/\n        when :uppercase\n          record.errors.add(attribute, \"en az bir b\u00fcy\u00fck harf i\u00e7ermeli\") unless value =~ \/[A-Z]\/\n        when :number\n          record.errors.add(attribute, \"en az bir rakam i\u00e7ermeli\") unless value =~ \/[0-9]\/\n        when :special\n          record.errors.add(attribute, \"en az bir \u00f6zel karakter (\u00f6rn: !@#$%) i\u00e7ermeli\") unless value =~ \/[^a-zA-Z0-9\\s]\/\n        end\n      end\n    end\n  end\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Gem'in ana dosyas\u0131 olan <code>lib\/my_custom_validators.rb<\/code>'yi bu validat\u00f6rleri otomatik olarak y\u00fckleyecek \u015fekilde d\u00fczenlememiz gerekiyor. Ayr\u0131ca Rails'in bu validat\u00f6rleri <code>validates :field, password_strength: {}<\/code> gibi bir syntax ile tan\u0131mas\u0131 i\u00e7in <code>ActiveModel<\/code>'e kendi validat\u00f6rlerimizi kaydetmeliyiz.<\/p>\n<pre><code>\n# lib\/my_custom_validators.rb\nrequire \"active_model\"\nrequire \"my_custom_validators\/version\"\nrequire \"my_custom_validators\/validators\/password_strength_validator\"\n# Di\u011fer validat\u00f6rlerinizi de buraya ekleyebilirsiniz.\n\nmodule MyCustomValidators\n  # ActiveModel'e kendi validat\u00f6r\u00fcm\u00fcz\u00fc kaydetmek i\u00e7in bir mod\u00fcl geni\u015fletmesi\n  # Bu k\u0131s\u0131m genellikle bir railtie i\u00e7inde yap\u0131l\u0131r, ancak basit bir gem i\u00e7in do\u011frudan da eklenebilir.\n  # Ancak Rails projeleri i\u00e7in en do\u011fru y\u00f6ntem Railtie kullanmakt\u0131r.\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Validat\u00f6r\u00fcn Active Model taraf\u0131ndan tan\u0131nmas\u0131 i\u00e7in <code>ActiveModel::Validations<\/code> mod\u00fcl\u00fcne dahil edilmesi gerekir. Bu i\u015flemi bir Rails uygulamas\u0131nda otomatik olarak yapmak i\u00e7in bir <code>railtie<\/code> kullanmak en iyi yoldur. Bu, Rails'in gem'inizi y\u00fckledi\u011finde belirli kodlar\u0131 \u00e7al\u0131\u015ft\u0131rmas\u0131n\u0131 sa\u011flar.<\/p>\n<pre><code>\n# lib\/my_custom_validators\/railtie.rb\nrequire 'rails\/railtie'\n\nmodule MyCustomValidators\n  class Railtie < Rails::Railtie\n    initializer \"my_custom_validators.active_record\" do |app|\n      ActiveSupport.on_load(:active_record) do\n        # T\u00fcm Active Record modellerine bu validat\u00f6rleri eklemek isterseniz buraya yazabilirsiniz.\n        # \u00d6rne\u011fin, PasswordStrengthValidator'\u0131 ActiveModel'e kaydetmek i\u00e7in\n        # ActiveModel::Validations.const_set(:PasswordStrengthValidator, MyCustomValidators::PasswordStrengthValidator)\n        # Ancak EachValidator tabanl\u0131 oldu\u011fu i\u00e7in Rails bunu otomatik olarak alg\u0131lar.\n        # Sadece <code>lib\/my_custom_validators\/validators<\/code> alt\u0131ndaki dosyalar\u0131 y\u00fckledi\u011fimizden emin olmal\u0131y\u0131z.\n        # Bu zaten <code>lib\/my_custom_validators.rb<\/code> i\u00e7inde yap\u0131l\u0131yor.\n      end\n    end\n  end\nend\n<\/pre>\n<p><\/code><\/p>\n<p><code>lib\/my_custom_validators.rb<\/code> dosyas\u0131nda, <code>railtie<\/code> dosyas\u0131n\u0131 da dahil etti\u011finizden emin olun:<\/p>\n<pre><code>\n# lib\/my_custom_validators.rb\nrequire \"active_model\"\nrequire \"my_custom_validators\/version\"\nrequire \"my_custom_validators\/railtie\" if defined?(Rails) # Sadece Rails ortam\u0131nda y\u00fcklensin\nrequire \"my_custom_validators\/validators\/password_strength_validator\"\n\nmodule MyCustomValidators\n  # Mod\u00fcl i\u00e7eri\u011fi\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Son olarak, <code>my_custom_validators.gemspec<\/code> dosyas\u0131n\u0131 a\u00e7\u0131n ve gem'inizin ad\u0131n\u0131, \u00f6zetini, a\u00e7\u0131klamas\u0131n\u0131 ve ba\u011f\u0131ml\u0131l\u0131klar\u0131n\u0131 g\u00fcncelleyin. \u00d6zellikle <code>add_dependency \"activesupport\", \">= 5.2\"<\/code> gibi Rails ba\u011f\u0131ml\u0131l\u0131klar\u0131n\u0131 ekledi\u011finizden emin olun, \u00e7\u00fcnk\u00fc <code>ActiveModel<\/code>'den t\u00fcremi\u015f validat\u00f6rler Rails'in par\u00e7as\u0131d\u0131r. Geli\u015ftirme ba\u011f\u0131ml\u0131l\u0131klar\u0131 aras\u0131na <code>rspec<\/code> ve <code>rubocop<\/code> gibi ara\u00e7lar\u0131 eklemeyi unutmay\u0131n.<\/p>\n<p>Bu ad\u0131mlarla, \u00f6zel validat\u00f6r\u00fcn\u00fcz art\u0131k bir gem olarak paketlenmeye haz\u0131r. Gem'i bir Rails projesine eklemek i\u00e7in, projenizin <code>Gemfile<\/code>'\u0131na <code>gem 'my_custom_validators', path: 'path\/to\/my_custom_validators_gem'<\/code> (yerel geli\u015ftirme i\u00e7in) veya <code>gem 'my_custom_validators', '~> 0.1.0'<\/code> (e\u011fer RubyGems.org'a yay\u0131nlad\u0131ysan\u0131z) sat\u0131r\u0131n\u0131 eklemeniz yeterli olacakt\u0131r. Daha sonra <code>bundle install<\/code> \u00e7al\u0131\u015ft\u0131rarak gem'i projenize dahil edebilirsiniz. Bu s\u00fcre\u00e7, kodunuzun mod\u00fclerli\u011fini ve yeniden kullan\u0131labilirli\u011fini maksimum d\u00fczeye \u00e7\u0131kararak geli\u015ftirme s\u00fcre\u00e7lerinizi verimli hale getirir.<\/p>\n<h2>Ger\u00e7ek D\u00fcnya Senaryosu: Harici API Entegrasyonlu Validat\u00f6r Gem'i<\/h2>\n<p>\u00d6zel validat\u00f6rlerin g\u00fcc\u00fcn\u00fc g\u00f6stermenin en iyi yollar\u0131ndan biri, karma\u015f\u0131k i\u015f gereksinimlerini kar\u015f\u0131lamak i\u00e7in harici servislerle entegrasyonu sa\u011flamakt\u0131r. Diyelim ki bir e-ticaret uygulaman\u0131z var ve \u00fcr\u00fcn SKU'lar\u0131n\u0131n (Stock Keeping Unit - Stok Tutma Birimi) yaln\u0131zca kendi veritaban\u0131n\u0131zda de\u011fil, ayn\u0131 zamanda harici bir envanter y\u00f6netim sisteminde de benzersiz ve ge\u00e7erli olmas\u0131 gerekiyor. Bu senaryo, API entegrasyonlu bir \u00f6zel validat\u00f6r gem'i olu\u015fturmak i\u00e7in m\u00fckemmel bir f\u0131rsatt\u0131r.<\/p>\n<h3>Senaryo: Harici Envanter Sistemi ile SKU Do\u011frulama<\/h3>\n<p>Bir <code>Product<\/code> modelimiz var ve <code>sku<\/code> ad\u0131nda bir \u00f6zelli\u011fi bulunuyor. Bu <code>sku<\/code>, a\u015fa\u011f\u0131daki kurallara uymal\u0131d\u0131r:<\/p>\n<ol>\n<li>Uygulaman\u0131n kendi veritaban\u0131nda benzersiz olmal\u0131d\u0131r (Rails'in standart <code>uniqueness<\/code> validat\u00f6r\u00fc ile kolayca yap\u0131labilir).<\/li>\n<li>Harici bir envanter y\u00f6netim sistemi API'si taraf\u0131ndan ge\u00e7erli ve mevcut olarak onaylanmal\u0131d\u0131r.<\/li>\n<\/ol>\n<p>Bu ikinci kural, \u00f6zel bir validat\u00f6r gerektirir. Harici API \u00e7a\u011fr\u0131lar\u0131 a\u011f gecikmelerine neden olabilece\u011fi i\u00e7in, validat\u00f6r\u00fcn performans ve hata y\u00f6netimi konusunda dikkatli olmas\u0131 gerekir. Ayr\u0131ca, API anahtarlar\u0131 gibi hassas bilgileri do\u011frudan validat\u00f6r kodunda tutmak yerine, Rails'in yap\u0131land\u0131rma mekanizmalar\u0131yla entegre etmeliyiz.<\/p>\n<h3>SkuAvailabilityValidator Gem'i Geli\u015ftirme<\/h3>\n<p>\u0130lk ad\u0131m olarak, gem yap\u0131m\u0131zda yeni bir validat\u00f6r dosyas\u0131 olu\u015fturaca\u011f\u0131z: <code>lib\/my_custom_validators\/validators\/sku_availability_validator.rb<\/code>.<\/p>\n<pre><code>\n# lib\/my_custom_validators\/validators\/sku_availability_validator.rb\nrequire 'net\/http'\nrequire 'json'\n\nmodule MyCustomValidators\n  class SkuAvailabilityValidator < ActiveModel::EachValidator\n    # API URL'ini ve anahtar\u0131n\u0131 yap\u0131land\u0131rma se\u00e7eneklerinden veya Rails uygulamas\u0131n\u0131n env'inden alabiliriz.\n    # Bu \u00f6rne\u011fi basitle\u015ftirmek ad\u0131na, varsay\u0131lan bir URL kullanaca\u011f\u0131z.\n    # Ger\u00e7ek uygulamada, Rails.application.credentials veya ENV de\u011fi\u015fkenleri tercih edilir.\n    \n    API_BASE_URL = options[:api_url] || \"https:\/\/api.externalinventory.com\/check_sku\"\n    API_KEY = options[:api_key] || ENV[\"EXTERNAL_INVENTORY_API_KEY\"]\n    \n    def validate_each(record, attribute, value)\n      return if value.blank? # Bo\u015f de\u011ferler i\u00e7in kontrol\u00fc atla, presence: true ile halledilir.\n\n      if sku_already_exists_in_app?(record, attribute, value)\n        record.errors.add(attribute, :taken, message: \"bu SKU zaten sistemde kay\u0131tl\u0131.\")\n        return\n      end\n\n      unless is_sku_available_via_api?(value)\n        record.errors.add(attribute, \"harici sistemde bulunamad\u0131 veya ge\u00e7erli de\u011fil.\")\n      end\n    rescue Timeout::Error, Errno::ECONNREFUSED, SocketError => e\n      # A\u011f sorunlar\u0131 veya API'nin eri\u015filememesi durumunda\n      record.errors.add(attribute, \"envanter sistemi ile ba\u011flant\u0131 kurulamad\u0131. L\u00fctfen daha sonra tekrar deneyin. (Hata: #{e.message})\")\n    rescue StandardError => e\n      # Di\u011fer beklenmedik hatalar\n      Rails.logger.error \"SKU do\u011frulama s\u0131ras\u0131nda beklenmedik hata: #{e.message}\"\n      record.errors.add(attribute, \"do\u011frulama s\u0131ras\u0131nda bir hata olu\u015ftu. L\u00fctfen y\u00f6neticinizle ileti\u015fime ge\u00e7in.\")\n    end\n\n    private\n\n    # Uygulama i\u00e7inde benzersizlik kontrol\u00fcn\u00fc validat\u00f6r seviyesinde yapmak (uniqueness validat\u00f6r\u00fcne ek olarak)\n    # veya do\u011frudan modelde uniqueness validat\u00f6r\u00fcn\u00fc kullanmak daha do\u011fru olur.\n    # Bu \u00f6rnek sadece bir ihtimali g\u00f6stermektedir.\n    def sku_already_exists_in_app?(record, attribute, value)\n      record.class.where(attribute => value).where.not(id: record.id).exists?\n    end\n\n    def is_sku_available_via_api?(sku)\n      uri = URI(\"#{API_BASE_URL}?sku=#{sku}&api_key=#{API_KEY}\")\n      \n      http = Net::HTTP.new(uri.host, uri.port)\n      http.use_ssl = (uri.scheme == 'https')\n      http.read_timeout = 5 # 5 saniye zaman a\u015f\u0131m\u0131\n\n      request = Net::HTTP::Get.new(uri)\n      response = http.request(request)\n\n      if response.is_a?(Net::HTTPSuccess)\n        data = JSON.parse(response.body)\n        data[\"available\"] == true # API'nin <code>{ \"sku\": \"ABC\", \"available\": true }<\/code> d\u00f6nd\u00fcrd\u00fc\u011f\u00fcn\u00fc varsayal\u0131m.\n      else\n        Rails.logger.warn \"Harici SKU kontrol API'sinden hata: #{response.code} #{response.message}\"\n        false\n      end\n    end\n  end\nend\n<\/pre>\n<p><\/code><\/p>\n<div class=\"expert-tip\">\n  Uzman \u0130pucu: Harici API \u00e7a\u011fr\u0131lar\u0131, uygulaman\u0131z\u0131n performans\u0131n\u0131 olumsuz etkileyebilir. Bu t\u00fcr validat\u00f6rlerde, API yan\u0131tlar\u0131n\u0131 ge\u00e7ici olarak \u00f6nbelle\u011fe almak (\u00f6rne\u011fin, Rails.cache kullanarak) veya asenkron do\u011frulama mekanizmalar\u0131 (arka plan i\u015fleri) kullanmak performans\u0131 %40 veya daha fazla art\u0131rabilir. Ayr\u0131ca, API anahtarlar\u0131n\u0131z\u0131 do\u011frudan koda g\u00f6mmek yerine <code>Rails.application.credentials<\/code> veya ortam de\u011fi\u015fkenleri (<code>ENV<\/code>) arac\u0131l\u0131\u011f\u0131yla y\u00f6netmek g\u00fcvenlik a\u00e7\u0131s\u0131ndan kritik \u00f6neme sahiptir.\n<\/div>\n<h3>Rails Uygulamas\u0131nda Validat\u00f6r Gem'ini Kullanma<\/h3>\n<p>Gem'imizi bir Rails uygulamas\u0131na entegre etmek i\u00e7in \u00f6ncelikle <code>Gemfile<\/code>'\u0131m\u0131za eklemeliyiz. E\u011fer gem'i yerel olarak geli\u015ftiriyorsak:<\/p>\n<pre><code>\n# Gemfile\ngem 'my_custom_validators', path: '~\/path\/to\/my_custom_validators_gem' # Yerel geli\u015ftirme yolu\n# veya e\u011fer gem'i RubyGems.org'a yay\u0131nlad\u0131ysan\u0131z:\n# gem 'my_custom_validators', '~> 0.1.0'\n<\/pre>\n<p><\/code><\/p>\n<p>Ard\u0131ndan <code>bundle install<\/code> \u00e7al\u0131\u015ft\u0131r\u0131n. \u015eimdi <code>Product<\/code> modelimizde bu validat\u00f6r\u00fc kullanabiliriz:<\/p>\n<pre><code>\n# app\/models\/product.rb\nclass Product < ApplicationRecord\n  validates :name, presence: true\n  validates :sku, presence: true, uniqueness: true # Kendi DB'mizdeki benzersizlik\n  validates :sku, sku_availability: { api_url: \"https:\/\/api.externalinventory.com\/check_sku\", api_key: ENV[\"EXTERNAL_INVENTORY_API_KEY\"] }\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Bu \u00f6rnekte, <code>SkuAvailabilityValidator<\/code>'a API URL'i ve API anahtar\u0131n\u0131 se\u00e7enek olarak ge\u00e7irdik. Bu, validat\u00f6r\u00fcn yap\u0131land\u0131r\u0131labilir olmas\u0131n\u0131 sa\u011flar. Art\u0131k bir \u00fcr\u00fcn olu\u015ftururken:<\/p>\n<pre><code>\n# Valid API'den gelen SKU\np1 = Product.create(name: \"Widget A\", sku: \"VALID-SKU-123\") # API'nin true d\u00f6nd\u00fcrd\u00fc\u011f\u00fcn\u00fc varsayal\u0131m\np1.valid? # => true\n\n# API'den ge\u00e7ersiz gelen SKU\np2 = Product.new(name: \"Gadget B\", sku: \"INVALID-SKU-XYZ\") # API'nin false d\u00f6nd\u00fcrd\u00fc\u011f\u00fcn\u00fc varsayal\u0131m\np2.valid? # => false\np2.errors.full_messages # => [\"Sku harici sistemde bulunamad\u0131 veya ge\u00e7erli de\u011fil.\"]\n\n# Ba\u011flant\u0131 hatas\u0131 olmas\u0131 durumunda (API kapal\u0131ysa)\np3 = Product.new(name: \"Tool C\", sku: \"ANY-SKU-ERROR\")\np3.valid? # => false\np3.errors.full_messages # => [\"Sku envanter sistemi ile ba\u011flant\u0131 kurulamad\u0131. L\u00fctfen daha sonra tekrar deneyin. (Hata: Connection refused - connect(2) for \"api.externalinventory.com\" port 443)\"]\n<\/pre>\n<p><\/code><\/p>\n<p>Bu ger\u00e7ek d\u00fcnya senaryosu, \u00f6zel validat\u00f6rlerin sadece basit kurallar\u0131 de\u011fil, ayn\u0131 zamanda harici sistemlerle etkile\u015fime giren karma\u015f\u0131k i\u015f mant\u0131\u011f\u0131n\u0131 da nas\u0131l kapsayabilece\u011fini net bir \u015fekilde g\u00f6stermektedir. Bu t\u00fcr bir yakla\u015f\u0131m, uygulaman\u0131z\u0131n mod\u00fclerli\u011fini art\u0131r\u0131rken, i\u015f kurallar\u0131n\u0131z\u0131n merkezile\u015ftirilmesine ve kolayca y\u00f6netilmesine olanak tan\u0131r.<\/p>\n<h2>Test Yazmak: \u00d6zel Validat\u00f6r Gem'inizi G\u00fcvenceye Almak<\/h2>\n<p>Yaz\u0131l\u0131m geli\u015ftirmede testler, kodunuzun g\u00fcvenilirli\u011fini ve do\u011fru \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131 sa\u011flaman\u0131n en \u00f6nemli yollar\u0131ndan biridir. \u00d6zel validat\u00f6r gem'leri i\u00e7in de bu durum ge\u00e7erlidir. Hatta, bir gem olarak tasarlanm\u0131\u015f yeniden kullan\u0131labilir kod bloklar\u0131 i\u00e7in test yazmak daha da kritik hale gelir, \u00e7\u00fcnk\u00fc bu kod bir\u00e7ok farkl\u0131 projede kullan\u0131labilecek potansiyele sahiptir. Testler, validat\u00f6r\u00fcn\u00fcz\u00fcn beklenen t\u00fcm senaryolarda do\u011fru hata mesajlar\u0131n\u0131 d\u00f6nd\u00fcrd\u00fc\u011f\u00fcn\u00fc ve kenar durumlar\u0131 (edge cases) d\u00fczg\u00fcn bir \u015fekilde ele ald\u0131\u011f\u0131n\u0131 garanti eder.<\/p>\n<h3>Neden \u00d6zel Validat\u00f6rleri Test Etmeliyiz?<\/h3>\n<ul>\n<li><strong>Do\u011fruluk:<\/strong> Validat\u00f6r\u00fcn i\u015f mant\u0131\u011f\u0131n\u0131n, belirlenen kurallara harfiyen uyup uymad\u0131\u011f\u0131n\u0131 do\u011frular.<\/li>\n<li><strong>Gerileme (Regression) \u00d6nleme:<\/strong> Yap\u0131lan de\u011fi\u015fikliklerin mevcut i\u015flevselli\u011fi bozmad\u0131\u011f\u0131ndan emin olunur.<\/li>\n<li><strong>Belgeleme:<\/strong> Testler, validat\u00f6r\u00fcn nas\u0131l \u00e7al\u0131\u015fmas\u0131 gerekti\u011fini g\u00f6steren canl\u0131 bir belge g\u00f6revi g\u00f6r\u00fcr.<\/li>\n<li><strong>G\u00fcven:<\/strong> Geli\u015ftiricilere, gem'i g\u00f6n\u00fcl rahatl\u0131\u011f\u0131yla kullanabilecekleri konusunda g\u00fcven verir.<\/li>\n<li><strong>Hata Mesajlar\u0131:<\/strong> Kullan\u0131c\u0131lara g\u00f6sterilen hata mesajlar\u0131n\u0131n do\u011fru ve anla\u015f\u0131l\u0131r oldu\u011fundan emin olunur.<\/li>\n<\/ul>\n<h3>RSpec ile Test Yazma<\/h3>\n<p>Ruby projelerinde test yazmak i\u00e7in RSpec pop\u00fcler ve g\u00fc\u00e7l\u00fc bir \u00e7er\u00e7evedir. Gem'inizi olu\u015fturdu\u011funuzda Bundler genellikle sizin i\u00e7in bir RSpec yap\u0131land\u0131rmas\u0131 haz\u0131rlar. \u015eimdi <code>PasswordStrengthValidator<\/code>'\u0131m\u0131z i\u00e7in nas\u0131l test yazaca\u011f\u0131m\u0131za bakal\u0131m.<\/p>\n<pre><code>\n# spec\/my_custom_validators\/validators\/password_strength_validator_spec.rb\nrequire 'spec_helper'\nrequire 'my_custom_validators\/validators\/password_strength_validator'\n\n# Test i\u00e7in ActiveModel'den t\u00fcretilmi\u015f basit bir model tan\u0131mlayal\u0131m\nclass ValidatableModel\n  include ActiveModel::Model\n  include ActiveModel::Attributes # Attributes API'sini kullanmak i\u00e7in\n  include ActiveModel::Validations\n\n  attr_accessor :password, :email # Validat\u00f6rlerin eri\u015fece\u011fi alanlar\n  attribute :password, :string\n  attribute :email, :string\n  \n  def initialize(attributes = {})\n    super\n    attributes.each { |name, value| send(\"#{name}=\", value) }\n  end\nend\n\nRSpec.describe MyCustomValidators::PasswordStrengthValidator do\n  let(:model_class) do\n    Class.new(ValidatableModel) do\n      validates :password, password_strength: { min_length: 10, requires: [:uppercase, :number, :special] }\n    end\n  end\n\n  it \"ge\u00e7erli bir parola ile ba\u015far\u0131l\u0131 olur\" do\n    model = model_class.new(password: \"GuclU_Parola1!\")\n    expect(model).to be_valid\n    expect(model.errors[:password]).to be_empty\n  end\n\n  it \"minimum uzunlu\u011fun alt\u0131nda parola ile ba\u015far\u0131s\u0131z olur\" do\n    model = model_class.new(password: \"Kisa1!\")\n    expect(model).not_to be_valid\n    expect(model.errors[:password]).to include(\"en az 10 karakter i\u00e7ermeli\")\n  end\n\n  it \"b\u00fcy\u00fck harf olmadan parola ile ba\u015far\u0131s\u0131z olur\" do\n    model = model_class.new(password: \"gucsuparola1!\")\n    expect(model).not_to be_valid\n    expect(model.errors[:password]).to include(\"en az bir b\u00fcy\u00fck harf i\u00e7ermeli\")\n  end\n\n  it \"say\u0131 olmadan parola ile ba\u015far\u0131s\u0131z olur\" do\n    model = model_class.new(password: \"GucluParola!\")\n    expect(model).not_to be_valid\n    expect(model.errors[:password]).to include(\"en az bir rakam i\u00e7ermeli\")\n  end\n\n  it \"\u00f6zel karakter olmadan parola ile ba\u015far\u0131s\u0131z olur\" do\n    model = model_class.new(password: \"GucluParola1\")\n    expect(model).not_to be_valid\n    expect(model.errors[:password]).to include(\"en az bir \u00f6zel karakter (\u00f6rn: !@#$%) i\u00e7ermeli\")\n  end\n\n  it \"bo\u015f parola ile ba\u015far\u0131s\u0131z olur\" do\n    model = model_class.new(password: \"\")\n    expect(model).not_to be_valid\n    expect(model.errors[:password]).to include(\"can't be blank\") # presence: true eklenmedi\u011fi i\u00e7in bu gelir\n  end\n\n  context \"farkl\u0131 validat\u00f6r se\u00e7enekleri ile\" do\n    let(:model_class_relaxed) do\n      Class.new(ValidatableModel) do\n        validates :password, password_strength: { min_length: 6, requires: [:lowercase, :number] }\n      end\n    end\n\n    it \"daha az k\u0131s\u0131tlay\u0131c\u0131 se\u00e7eneklerle ge\u00e7erli olur\" do\n      model = model_class_relaxed.new(password: \"sifre1\")\n      expect(model).to be_valid\n    end\n\n    it \"daha az k\u0131s\u0131tlay\u0131c\u0131 se\u00e7eneklerle b\u00fcy\u00fck harf olmadan ba\u015far\u0131s\u0131z olmaz\" do\n      model = model_class_relaxed.new(password: \"sifre1\")\n      expect(model).to be_valid\n      expect(model.errors[:password]).not_to include(\"en az bir b\u00fcy\u00fck harf i\u00e7ermeli\")\n    end\n  end\nend\n<\/pre>\n<p><\/code><\/p>\n<p>Yukar\u0131daki test blo\u011fu, <code>PasswordStrengthValidator<\/code>'\u0131n farkl\u0131 senaryolarda nas\u0131l davrand\u0131\u011f\u0131n\u0131 kapsar. Ge\u00e7erli parolalar, eksik karakter t\u00fcrleri olan parolalar ve bo\u015f parolalar i\u00e7in testler yaz\u0131lm\u0131\u015ft\u0131r. Ayr\u0131ca, farkl\u0131 validat\u00f6r se\u00e7enekleriyle (<code>min_length<\/code>, <code>requires<\/code>) nas\u0131l \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131 do\u011frulamak i\u00e7in ba\u011flamlar (<code>context<\/code>) kullan\u0131lm\u0131\u015ft\u0131r. Testleri \u00e7al\u0131\u015ft\u0131rmak i\u00e7in gem'in ana dizininde <code>bundle exec rspec<\/code> komutunu kullanabilirsiniz.<\/p>\n<p>Harici API entegrasyonlu validat\u00f6rler i\u00e7in test yazarken, API \u00e7a\u011fr\u0131lar\u0131n\u0131 \"mock\" veya \"stub\" etmeniz kritik \u00f6neme sahiptir. Bu, ger\u00e7ek API'ye ba\u011f\u0131ml\u0131 olmadan validat\u00f6r\u00fcn kendi mant\u0131\u011f\u0131n\u0131 test etmenizi sa\u011flar ve testlerinizin h\u0131zl\u0131 ve g\u00fcvenilir olmas\u0131n\u0131 garantiler. \u00d6rne\u011fin, <code>SkuAvailabilityValidator<\/code> i\u00e7in <code>is_sku_available_via_api?<\/code> metodunu taklit ederek, API'nin farkl\u0131 yan\u0131tlar\u0131n\u0131 (ba\u015far\u0131l\u0131, ba\u015far\u0131s\u0131z, hata) sim\u00fcle edebiliriz. Bu yakla\u015f\u0131m, gem'inizin sadece geli\u015ftirme s\u0131ras\u0131nda de\u011fil, gelecekteki g\u00fcncellemelerde de sa\u011flam kalmas\u0131n\u0131 sa\u011flar.<\/p>\n<h2>Sonu\u00e7: Uygulamalar\u0131n\u0131z\u0131 \u00d6zel Validat\u00f6rlerle G\u00fc\u00e7lendirin<\/h2>\n<p>Bu makale boyunca, Ruby on Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 nas\u0131l bir \u00fcst seviyeye ta\u015f\u0131yaca\u011f\u0131n\u0131z\u0131 detayl\u0131 bir \u015fekilde inceledik. Standart Rails validat\u00f6rlerinin yetersiz kald\u0131\u011f\u0131 durumlarda, \u00f6zel validat\u00f6rlerin kurtar\u0131c\u0131 rol\u00fcn\u00fc, nas\u0131l ad\u0131m ad\u0131m olu\u015fturulacaklar\u0131n\u0131 ve hatta bunlar\u0131 bir gem olarak paketleyerek yeniden kullan\u0131labilirlik ve s\u00fcrd\u00fcr\u00fclebilirlik a\u00e7\u0131s\u0131ndan nas\u0131l maksimum fayda sa\u011flayaca\u011f\u0131m\u0131z\u0131 \u00f6\u011frendik. Karma\u015f\u0131k i\u015f kurallar\u0131n\u0131 basitle\u015ftirmekten, harici API entegrasyonlar\u0131yla veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc sa\u011flamaya kadar, \u00f6zel validat\u00f6rler geli\u015ftirici ara\u00e7 setinizin vazge\u00e7ilmez bir par\u00e7as\u0131 olmal\u0131d\u0131r.<\/p>\n<p>\u00d6zel validat\u00f6rler, kodunuzu daha mod\u00fcler, okunabilir ve test edilebilir hale getirerek uygulama kalitenizi art\u0131r\u0131r. Bir validat\u00f6r\u00fc gem olarak yay\u0131mlamak, hem kendi projelerinizde tutarl\u0131l\u0131\u011f\u0131 sa\u011flar hem de Ruby toplulu\u011funa katk\u0131da bulunma f\u0131rsat\u0131 sunar. Unutmay\u0131n ki, her zaman en karma\u015f\u0131k \u00e7\u00f6z\u00fcme gitmeye gerek yoktur; bazen basit bir <code>ActiveModel::Validator<\/code> yeterli olurken, baz\u0131 durumlarda <code>ActiveModel::EachValidator<\/code>'\u0131n esnekli\u011fi kritik \u00f6neme sahip olabilir. Se\u00e7iminiz ne olursa olsun, do\u011fru ve eksiksiz testler yazmak, validat\u00f6rlerinizin gelecekte de g\u00fcvenilirli\u011fini garanti alt\u0131na alman\u0131n anahtar\u0131d\u0131r.<\/p>\n<p>Art\u0131k Rails uygulamalar\u0131n\u0131zda daha g\u00fc\u00e7l\u00fc, daha temiz ve daha esnek do\u011frulama mekanizmalar\u0131 olu\u015fturmak i\u00e7in gerekli bilgi ve ara\u00e7lara sahipsiniz. Bu bilgileri kullanarak, projelerinizdeki veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc korurken geli\u015ftirme s\u00fcre\u00e7lerinizi optimize edebilirsiniz.<\/p>\n<h3>S\u0131k\u00e7a Sorulan Sorular (SSS)<\/h3>\n<h4 id=\"faq-1\">1. \u00d6zel validat\u00f6rler performans\u0131 etkiler mi?<\/h4>\n<p>Evet, \u00f6zellikle harici API \u00e7a\u011fr\u0131lar\u0131 veya yo\u011fun hesaplamalar i\u00e7eren \u00f6zel validat\u00f6rler performans\u0131 etkileyebilir. Bu t\u00fcr validat\u00f6rler i\u00e7in \u00f6nbellekleme (caching) teknikleri kullanmak veya asenkron do\u011frulama (arka plan i\u015fleri arac\u0131l\u0131\u011f\u0131yla) uygulamak performans\u0131 \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131rabilir. Basit, dahili do\u011frulay\u0131c\u0131lar genellikle g\u00f6zle g\u00f6r\u00fcl\u00fcr bir performans d\u00fc\u015f\u00fc\u015f\u00fcne neden olmaz.<\/p>\n<h4 id=\"faq-2\">2. Birden fazla validat\u00f6r\u00fc ayn\u0131 modele uygulayabilir miyim?<\/h4>\n<p>Kesinlikle! Bir model, hem standart Rails validat\u00f6rlerini (<code>presence: true<\/code>, <code>uniqueness: true<\/code> vb.) hem de bir\u00e7ok \u00f6zel validat\u00f6r\u00fc ayn\u0131 anda kullanabilir. \u00d6rne\u011fin, bir kullan\u0131c\u0131n\u0131n e-postas\u0131n\u0131 \u00f6zel bir format validat\u00f6r\u00fc ile do\u011frular ve ayn\u0131 zamanda benzersizli\u011fini standart Rails validat\u00f6r\u00fc ile kontrol edebilirsiniz.<\/p>\n<h4 id=\"faq-3\">3. Validat\u00f6rler i\u00e7inde veritaban\u0131 sorgusu yapmak uygun mudur?<\/h4>\n<p>Evet, belirli durumlarda uygun olabilir, ancak dikkatli olunmal\u0131d\u0131r. \u00d6rne\u011fin, bir kayd\u0131n ba\u015fka bir veritaban\u0131 tablosundaki belirli bir durumla uyumlu olup olmad\u0131\u011f\u0131n\u0131 kontrol etmek gerekebilir. Ancak, her do\u011frulama \u00e7a\u011fr\u0131s\u0131nda yap\u0131lan yo\u011fun sorgular performans sorunlar\u0131na yol a\u00e7abilir. Bu t\u00fcr durumlarda, sorgular\u0131 optimize etmek, indeksler kullanmak veya m\u00fcmk\u00fcnse verileri \u00f6nbelle\u011fe almak \u00f6nemlidir.<\/p>\n<h4 id=\"faq-4\">4. <code>EachValidator<\/code> ile <code>Validator<\/code> aras\u0131ndaki fark nedir?<\/h4>\n<ul>\n<li><code>ActiveModel::Validator<\/code>: Modelin genel durumunu do\u011frulamak i\u00e7in kullan\u0131l\u0131r. <code>validate(record)<\/code> metodunu uygular ve modelin tamam\u0131na uygulan\u0131r (<code>validates_with MyValidator<\/code>).<\/li>\n<li><code>ActiveModel::EachValidator<\/code>: Modelin belirli bir \u00f6zelli\u011finin de\u011ferini do\u011frulamak i\u00e7in kullan\u0131l\u0131r. <code>validate_each(record, attribute, value)<\/code> metodunu uygular ve do\u011frudan bir \u00f6zelli\u011fe (veya birka\u00e7 \u00f6zelli\u011fe) uygulan\u0131r (<code>validates :field, my_each_validator: { option: value }<\/code>). Genellikle daha esnektir ve se\u00e7enekler kabul edebilir.<\/li>\n<\/ul>\n<h4 id=\"faq-5\">5. \u00d6zel validat\u00f6rler g\u00fcvenlik riskleri ta\u015f\u0131r m\u0131?<\/h4>\n<p>Yanl\u0131\u015f yaz\u0131lm\u0131\u015f \u00f6zel validat\u00f6rler g\u00fcvenlik riskleri ta\u015f\u0131yabilir. \u00d6zellikle harici servislerle etkile\u015fime giren validat\u00f6rler (\u00f6rn: API anahtarlar\u0131n\u0131n a\u00e7\u0131kta b\u0131rak\u0131lmas\u0131, SQL enjeksiyonuna a\u00e7\u0131k sorgular, k\u00f6t\u00fc niyetli veri i\u015fleme) dikkatli yaz\u0131lmal\u0131d\u0131r. Daima hassas bilgileri g\u00fcvenli bir \u015fekilde y\u00f6netin (ENV, Rails credentials), girdi verilerini sanitize edin ve potansiyel g\u00fcvenlik a\u00e7\u0131klar\u0131n\u0131 minimize etmek i\u00e7in g\u00fcvenilir k\u00fct\u00fcphaneler kullan\u0131n. Ayr\u0131ca, hata mesajlar\u0131nda a\u015f\u0131r\u0131 detay vermekten ka\u00e7\u0131n\u0131n, bu da potansiyel sald\u0131rganlara bilgi sa\u011flayabilir.<\/p>\n<style>\n  \/* Mobil uyumlu HTML \u00f6rne\u011fi: Do\u011frulama hata mesajlar\u0131n\u0131n nas\u0131l g\u00f6r\u00fcnece\u011fini ayarlar *\/\n  .validation-errors {\n    border: 1px solid #e0b4b4;\n    background-color: #fcebeb;\n    color: #a00;\n    padding: 15px;\n    margin-bottom: 20px;\n    border-radius: 4px;\n    font-size: 0.9em;\n  }<\/p>\n<p>  \/* Daha k\u00fc\u00e7\u00fck ekranlarda hata mesajlar\u0131n\u0131n stilini optimize et *\/\n  @media (max-width: 768px) {\n    .validation-errors {\n      padding: 10px;\n      margin-left: 10px;\n      margin-right: 10px;\n      font-size: 0.85em;\n    }\n  }<\/p>\n<p>  .expert-tip {\n    background-color: #e6f7ff;\n    border-left: 5px solid #1890ff;\n    padding: 15px 20px;\n    margin: 20px 0;\n    font-style: italic;\n    color: #262626;\n    border-radius: 4px;\n  }<\/p>\n<p>  .expert-tip strong {\n    color: #1890ff;\n  }\n<\/style>\n<p><\/body><\/p>\n","protected":false},"excerpt":{"rendered":"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi&hellip;","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_page_header_type":"","csco_page_load_nextpost":"","csco_page_subscribe_form":"","csco_page_contact_form":"","footnotes":""},"categories":[1342],"tags":[],"class_list":{"0":"post-34430","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-ai","7":"cs-entry","8":"cs-video-wrap"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.5 (Yoast SEO v25.3.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi<\/title>\n<meta name=\"description\" content=\"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi\" \/>\n<meta property=\"og:description\" content=\"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\" \/>\n<meta property=\"og:site_name\" content=\"Kodlar\u0131n Gizemli D\u00fcnyas\u0131\" \/>\n<meta property=\"article:published_time\" content=\"2025-11-16T12:01:14+00:00\" \/>\n<meta name=\"author\" content=\"Fatih Soysal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"Fatih Soysal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"27 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\"},\"author\":{\"name\":\"Fatih Soysal\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"headline\":\"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi\",\"datePublished\":\"2025-11-16T12:01:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\"},\"wordCount\":3472,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"articleSection\":[\"AI\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#respond\"]}],\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\",\"name\":\"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\"},\"datePublished\":\"2025-11-16T12:01:14+00:00\",\"description\":\"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.\",\"breadcrumb\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Anasayfa\",\"item\":\"https:\/\/fatihsoysal.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/\",\"name\":\"Fatihsoysal.com\",\"description\":\"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim\",\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\",\"name\":\"Fatih Soysal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"contentUrl\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"width\":512,\"height\":512,\"caption\":\"Fatih Soysal\"},\"logo\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\"},\"description\":\"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi","description":"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/","og_locale":"tr_TR","og_type":"article","og_title":"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi","og_description":"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.","og_url":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/","og_site_name":"Kodlar\u0131n Gizemli D\u00fcnyas\u0131","article_published_time":"2025-11-16T12:01:14+00:00","author":"Fatih Soysal","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"Fatih Soysal","Tahmini okuma s\u00fcresi":"27 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#article","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/"},"author":{"name":"Fatih Soysal","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"headline":"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi","datePublished":"2025-11-16T12:01:14+00:00","mainEntityOfPage":{"@id":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/"},"wordCount":3472,"commentCount":0,"publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"articleSection":["AI"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#respond"]}],"copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/fatihsoysal.com\/blog\/#organization"}},{"@type":"WebPage","@id":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/","url":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/","name":"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/#website"},"datePublished":"2025-11-16T12:01:14+00:00","description":"Rails uygulamalar\u0131n\u0131zda model do\u011frulamas\u0131n\u0131 bir \u00fcst seviyeye ta\u015f\u0131mak m\u0131 istiyorsunuz? Bu kapsaml\u0131 rehber, \u00f6zel validat\u00f6rleri gem olarak paketlemeyi ad\u0131m ad\u0131m a\u00e7\u0131kl\u0131yor, kod kalitenizi art\u0131r\u0131yor ve veri b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc g\u00fc\u00e7lendiriyor.","breadcrumb":{"@id":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fatihsoysal.com\/blog\/ruby-on-rails-modelleri-icin-ozel-dogrulayicilar-gelistirme-rehberi\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Anasayfa","item":"https:\/\/fatihsoysal.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Ruby on Rails Modelleri \u0130\u00e7in \u00d6zel Do\u011frulay\u0131c\u0131lar Geli\u015ftirme Rehberi"}]},{"@type":"WebSite","@id":"https:\/\/fatihsoysal.com\/blog\/#website","url":"https:\/\/fatihsoysal.com\/blog\/","name":"Fatihsoysal.com","description":"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim","publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1","name":"Fatih Soysal","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","contentUrl":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","width":512,"height":512,"caption":"Fatih Soysal"},"logo":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/"},"description":"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.","url":"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/"}]}},"yoast_meta":{"yoast_wpseo_title":"","yoast_wpseo_metadesc":"","yoast_wpseo_canonical":""},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/34430","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/comments?post=34430"}],"version-history":[{"count":0,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/34430\/revisions"}],"wp:attachment":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/media?parent=34430"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/categories?post=34430"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/tags?post=34430"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}