İletişim sayfası
Uygulamamıza İletişim sayfası ekleyerek devam edeceğiz. Öncelikle test rutinlerini ekleyelim.
test/controllers/static_pages_controller_test.rb
Tabi ki test yaparsak hata verecektir.
Aynı daha önce about eylemi için yaptıklarımızı tekrarlayacağız. Öncelikle yönlendirmesini yapalım.
config/routes.rb
Şimdi de kontrolöre eylemi ekleyelim.
app/controllers/static_pages_controller.rb
Şimdi bir de görsel sayfası ekleyelim.
app/views/static_pages/contact.html.erb
Artık uygulamamız testten geçiyor durumda. İletişim sayfası eklendi.
Rails yönlendirmeler
İsimlendirilmiş yönlendirmeler için tabi ki uygulamamızın yönlendirmelerinin yapıldığı config/routes.rb dosyası kullanılır.
Bu satır uygulamamızın / ana root adresini static_pages#home eylemine yönlendiriyor. Bu şekil yönlendirmenin bir önemli etkisi daha var. Yönlendirmeleri düz URL değeri yerine isimlendirilmiş yönlendirmelerle yapmak. Uygulamamızda kullanabileceğimiz iki isimlendirilmiş yönlendirme var, root_path ve root_url değişkenleri.
path değişken uygulama root'una göre bağıl, url değişken ise tam url web adresi olur. Normalde redirect hariç path değişkenler kullanacağız.
Şimdi diğer sayfalar için de isimlendirilmiş yönlendirme yapmak için routes.rb dosyamızı değiştirelim.
bunun yerine
yapmalıyız. Bu sayede help_path ve help_url değişkenleri oluşur. Hepsi için uygularsak
config/routes.rb
Yönlendirmeler değiştiği için testlerimiz hata verecektir. Onları da bir düzenleyelim.
static_pages_controller_test.rb
İsimlendirilmiş yönlendirmeleri kullanalım
Yönlendirmeleri düzenleyerek artık görsellerimizdeki linklerde isimlendirilmiş yönlendirmeleri kullanabiliriz. link_to metodlarına verdiğimiz ikinci argümanlardaki "#" değerini artık değiştirebiliriz.
yerine
yazabiliriz.
Kısmi görselimiz _header.html.erb görselinden başlayalım.
app/views/layouts/_header.html.erb
Sadece Giriş Yap linkini sonraya bıraktık, çünkü daha şifreli kullanıcı girişleri konusuna çok var. Ana sayfa için root_path ve yardım sayfası için help_path değişkenlerini kullanıyoruz.
Sırada ikinci kısmi görselimiz _footer.html.erb dosyası var.
app/views/layouts/_footer.html.erb
Burada da Hakkımızda sayfası için about_path ve İletişim sayfası için contact_path değişkenlerini kullanıyoruz. Burada Haberler linkinde de target: "_blank" öznitelik değerini verdik, <a> elemanlarında target="_blank" özelliği bağlantının yeni sekmede açılmasını sağlar. Burada da Haberler bağlantısı bir dış siteye gönderdiği için yeni bir sekmede açılmasını sağladık.
Yerleşimdeki linkleri test etmek
Yerleşim görsel dosyamızdaki linkleri değiştirdik, şimdi bir test kodu yazarak sağlıklı çalıştıklarını görelim. Bunu tarayıcımızda linkleri tek tek tıklayarak deneyebiliriz. Ama burada amacımız üzüm yemek değil, test işlerini öğrenmeye çalışıyoruz.
Bu adımda bir integration_test üreterek başlayacağız. Bunlar uygulamamızın davranışları konusunda uçtan uca testler yazmamıza yardımcı olur. Adını site_layout koyduğumuz bir test şablonunu Rails'e ürettirmek için terminalde şu komutu girelim.
rails g komutunun rails generate komutu ile aynı işi yaptığını söylemiş miydim? Dağlara taşlara bir komut, üretmediği şey yok.
Dikkat ettiyseniz Rails ürettiği test dosyasının adına otomatik olarak _test ilavesi getirdi.
Planımız şu:
- Ana sayfayı çağırmak
- Doğru görsel şablonuın yayınlandığından emin olmak
- Ana Sayfa, Yardım, Hakkımızda ve İletişim linklerinin doğruluğunu görmek
test/integration/site_layout_test.rb
Bu değişikliklerden sonra test çalıştırınca bir hata aldım ama testle alakalı değil
assert_template metodu başka bir gem içine taşınmış ve bunu da Gemfile içine dahil edip bundle install yapmamız gerekiyormuş. Ben Gemfile içine bir değişiklik yapacaksam hemen uygulama klasörümün bir yedeğini alıyorum, size de tavsiye ederim.
Haydi Gemfile içine mesajda geçen gem'i ekleyelim.
Gemfile
ve bundle çalıştıralım
Gem yüklendikten sonra testimizi tekrar çalıştıralım.
Ana Sayfa'da 2 tane root_path olması lazımdı biri logoya tıklayınca diğeri de navbar'ın üzerindeki Ana Sayfa yazısı, ama bir tane bulmuş testimiz. Eksiği _header.html.erb kısmi görselinde buldum.
Logodaki linki unutmuşum,
olması gerekiyordu, düzeltip test çalıştırdım, sonuç başarılı oldu. Bu test kodundaki satırlara bir bakalım.
İsteği yapılan sayfa için "static_pages/home" görsel dosyasının yani home.html.erb dosyasının yayınlanması beklentisini ifade ediyor.
<a> elemanları içinde <a href="/"> özelliğine sahip olan elemanlar bekliyor. count: 2 ile de bu beklentiye uyan 2 tane eleman olması beklendiği belirtiliyor. Bu assert_select metodunun birkaç örnek kullanımını görelim.
Sadece entegrasyon testlerinin çalışması için
Kullanıcı kayıt işlemine giriş
Kullanıcılara bir giriş olsun diye bu bölümde bir kayıt olma sayfası linki ekleyeceğiz. Tamamlanması daha zaman alacak. Yeni bir kontrolörle başlayacağız, ilerleyen bölümlerde User modeli ve kayıt işlemlerini yapacağız.
Users kontrolörü
Bundan önce Static Pages kontrolörümüzü oluşturduk Sıra ikinci kontrolörümüz Users kontrolörüne geldi. Daha önce olduğu gibi generate kullanacağız, ama sadece temel olarak yeni kayıt eylemi olacak şakilde. Rails REST ilkelerine uygun olarak yeni kullanıcı kayıt sayfasını new eylemine bağlayacağız.
Bu komutla Users kontrolörü ve eylemleri dosyası, new eylemi için görsel dosyası ve kontrolör için bir test dosyası oluşturulur.
app/controllers/users_controller.rb
app/views/users/new.html.erb
test/controllers/users_controller_test.rb
Bu noktada test çalıştırırsak sorunsuz geçmelidir.
Signup URL değeri
Yönlendirme dosyamızda
satırı eklenmiş olmalıdır. Bunu "/signup" bağlantısını alacak ve isimlendirilmiş yönlendirme olacak şekilde, diğerleri gibi düzenleyelim.
config/routes.rb
Tabii ki test çakılacak hemen düzenleyelim
test/controllers/users_controller_test.rb
Şimdi Ana Sayfa'daki "Hemen Katılın!" yazılı butonun linkini isimlendirilmiş yönlendirme kullanır şekle getirelim.
app/views/static_pages/home.html.erb
Son olarak Kayıt Ol sayfasına kaba bir görünüm ekleyelim.
app/views/users/new.html.erb
Artık Ana Sayfada Kayıt Olun butonu tıklanınca bu geçici görselimiz açılacaktır.
Bu bölümde öğrendiklerimiz
- Html5 kulanarak header, footer, logo ve gövde içeriği olan sayfalar yapabiliriz.
- Rails kısmi görselleri karmaşık görsel dosya kodlarını güzelleştirmek için çok faydalıdır.
- CSS kuralları yazarak görsellerimizin stillerini değiştirebiliriz.
- Bootstrap kütüphanesi kullanarak kolayca güzel görünümlü web sayfaları yapabiliriz.
- Sass ve asset pipeline sayesinde tasarım esnasında ve üretim modunda ayrı CSS dosyalama teknikleri ile uğraşmadan performanslı uygulama geliştirebiliriz.
- Rails ile kemdi yönlendirmelerimizi yapabilir, isimlendirilmiş yönlendirmeler kullanabiliriz.
- Entegrasyon testleri ile tarayıcıda sayfadan sayfaya geçiş tıklamaları test edilebilir.
Kullanıcıları Düzenlemek
En son yeni kullanıcılar eklemek için bir sayfa temelini oluşturmuştuk. Bundan sonra bu Kayıt Olun sayfası üzerinden öğrenmeye devam edeceğiz. Bu bölümde ilk adımımız data modelimiz olan User modelini tanımlayacağız ve oraya veri atmak için düzeneği kuracağız. Sonraki bölümlerde kullanıcının kayıt olup kullanıcı sayfasını hazırlamasını gerçekleştireceğiz.
Kendi yetkilendirme sisteminizi tasarlamak
Genelde tüm web uygulamalarında bir çeşit yetkilendirme sistemi olur. Sonuç olarak birçok web framework bu işleri görecek kütüphanelere sahiptir, Rails de öyledir. En yaygın kullanılan Devise gem'dir ve güçlü uygulamalar yapmakta kullanılır. Ancak öğrenmek amacımız olduğu için ve bu tip kütüphanelerin veri yapılarını anlamak ustalar için bile zor olduğu için, kendimize göre bir yol izleyeceğiz.
Ancak tüm öğrendikleriniz sonunda hala 3. parti birilerinin geliştirdiği sistemi kullanmaya karar verirseniz en iyi seçim Devise olacaktır.
User modeli
Hedefimiz yeni kullanıcıların kaydı olduğuna göre, kullanıcı bilgilerini saklayacak bir yere ihtiyacımız var. Kayıt sayfasına bir taslak hazırlarken toplayacağımız verileri de bir düşünelim.
Rails'de bir veri kaydı yapısını tarif ederken Model denir (MVC'deki M). Rails kalıcı verileri bir veritabanında saklamak için Active Record kütüphanesini kullanır. Active Record sınıfı verilerin bir bağıl veri tabanında saklanması , sorgulanması, değiştirilmesi, silinmesi vs SQL sorgulamaları yapmak için birçok metodlara sahiptir. Bundan başka Rails migration kodları sayesinde SQL data definition language (DLL) bilmeden saf Ruby kodları ile veri tabanında tanımlamaları kolayca yapabilirsiniz. Bu sayede geliştirme yaparken SQLite veri tabanı üretim modunda PostgreSQL veri tabanı kullanırken ayrı ayrı kodlar yazmanız gerekmez.
Veri tabanı migrasyonları
Daha önce bir denemede örnek bir User sınıfını name ve email özellikleri ile tanımlamıştık.
Rails konsolda bu sınıfı kullanarak ürettiğimiz User nesneleri konsolu kapatınca yok oluyor (çöp oluyor) tabii ki. Bu bölümde amacımız kullanıcılar için bir model oluşturarak verilerin öyle kolayca yok olmasını önlemek.
Daha önce tanımladığımız User sınıfı gibi Rails modelimizi tanımlarken de name ve email öznitelikleri ile başlayacağız, ama bir fark var, kullanıcı adı (name) kayıtlarda sadece bir tane aynısından olacak. Şifre için ilaveyi daha sonra yapacağız.
Rails'de kullanıcıların modellemesini yaparken User sınıfı tanımında kullandığımız gibi attr_accessor metodu ile öznitelikleri belirtmiyoruz, bunun yerine modelimiz veri tabanındaki bir tabloya işaret ederken öznitelikler de o tablonun sütun başlıklarını gösterir. Örneğin isimleri ve email adresleri olan kullanıcıları üretmek için users tablosunu name ve email sütunları ile oluşturacağız. Bu tablodaki her satır bir kullanıcı kaydına karşı gelecektir. Hayalimizde şöyle kayıtları olan bir veri tablosu var.
Bu tablo için düşündüğümüz tablo yapısı ise
Rails modeli olarak bu tabloyu yaparsak elde edeceğimiz tablo ise
Daha önce kontrolör eklemek için
kullandığımız gibi generate model ile de bir model ekleyebiliriz.
Burada dikkat edilmesi gereken nokta kullanıcılar için tasarlanan model için tek bir kaydı ifade eden User teriminin kullanılması (Users değil). Kontrolör adı Users fakat model adı User olmalı. Argümanlarda name:string ve email:string vererek tablomuzda string değer olan name ve email sütunları olmasını istediğimiz ifade ediyoruz.
Terminalde girdiğimiz generate komutunun ürettiği dosyalardan biri de db/migrate/ klasörü içinde ürettiği ..._create_users.rb migrasyon dosyası. Bu dosyayı kullanarak veri tabanında gereken tablo ilavemizi yaparız. Şimdi içine bir bakalım.
db/migrate/[timestamp]_create_users.rb
Migrasyon dosyasının adı üretildiği zamanı ifade eden bir değerle başlıyor. Mesela bendeki adı 20250404135314_create_users.rb . Yıl-ay-gün-saat-dakika-saniye birleşimi bu notasyonu ben tüm projelerimde yedek alırken .zip dosyalarının isimlerine eklerim ki geriye dönmem gerekirse istediğim tarihi bulabileyim. Rails kullanırken edinilen bu mantıklı isimlendirmeyi ekip arkadaşlarıma zorunlu yaptırıyorum.
Migrasyon kodunda tanımlanan change metodu Rails'e veri tabanında bir değişim yapılacağını bildirir. Bu migrasyonda change metodu içinde create_table ile kullanıcılar için users adında yeni bir tablo ekleniyor. create_table metodu bir blok alıyor ve blok değişkeni olarak t değişkeni eklenecek olan users tablosunu işaret ediyor. Metod t değişkenini tabloya name ve email sütunlarını eklemek için kullanılıyor, her ikisi de string tipinde veriler içerecek. Buradaki tablo adı çoğul (users) ve modeldeki isim tekil (user), bu Rails'in bir geleneği, bir model tek bir kullanıcıyı ifade ederken tablomuzda birçok kullanıcının kaydı olacak. Kod bloğundaki son satırda t.timestamps yazıyor. bu özel metod created_at ve updated_at adında başlığı olan iki sütunu tabloya ekler. Bu iki sütunda sırasıyla kaydın eklendiği zaman ve en son değiştirildiği zaman bilgileri otomatik olarak Rails tarafından doldurulacak.
Şimdi bu migrasyonu (yani birleştirmeyi) gerçekleştirip veri tabanımızın kullanıcılar için tablo sahibi olması için terminalde,
Dikkat edin db:migrate, benim hep düştüğüm hata gibi db/migrate değil. Eğer db:migrate komutunu ilk defa çalıştırırsak (ki Yeni App içinde ilk oldu) storage/development.sqlite3 adında bir veri tabanı dosyası oluşturur. Bu bir SQLite veri tabanıdır. Bende DB Browser for SQLite diye bir portatif uygulama var, bununla açınca veri tabanında users tablosu yapılanması
ve tabii ki şu anda içinde bir veri kaydı bulunmuyor.
Burada migrasyon dosyasında bile olmayan id sütunu Rails tarafından eklenir ve tüm tablolarda otomatik olarak eklenerek aynı değerlere sahip ama başka satırlarda girilmiş kayıtların birbirinden otomatik ayrılmasını sağlıyor. Bu sütuna girilen değer her kayıt girildikçe otomatik artar ve geri kalan değerler aynı olsa bile id değerleri hep farklı olur. Girilebilecek sayının üst limitine gelince ne olur? onu şimdilik bilmiyorum.
Model dosyası
rails g model komutu ile oluşturduğumuz User modeli dosyasına bir bakalım.
app/models/user.rb
Burada User modelimizin sınıfının Rails'in ApplicationRecord sınıfından türetildiğini görüyoruz. Aslında o da ActiveRecord::Base sınıfından türetilmiştir. ActiveRecord::Base sınıfı içinde neler olduğunu anlamaya bazı örneklerle başlayalım.
User nesnelerini üretmek
Denemlerimizi Rails konsolda yapacağız, ancak şimdilik veri tabanına yeni kayıtlar eklememek için konsolu sandbox modunda başlatacağız.
Bilgilendirme mesajından anlaşıldığı üzere yapılan tüm değişiklikler çıkışta geri alınacaktır. Daha önce konsolda User.new metodu kullanarak kullanıcı eklemesini görmüştük, ama bir sınıf tanımlaması kullanarak. Model kullanarak benzerini yaparken sınıf dosyasını yüklememize gerek yok, Rails otomatik olarak uygulamada tanımlı sınıf dosyalarını konsola getirecektir. Bu durumda direk olarak yeni User nesnesi üretebiliriz.
Konsolda bir User nesnesinin nasıl gösterildiği, komutumuza dönen cevapta görülüyor. Argüman vermeden User.new metodu çağırdığımızda tüm öznitelikleri nil olan bir User nesnesi üretiliyor. Daha önce kendi sınıf tanımımızla User nesnesi üretirken argümanda bir hash değer vererek öznitelik değerlerini belirtmiştik. Model kullanırken de aynı davranılır.
email bilgisine gösterimde [FILTERED] gelmesi ilginç. config/initializers/filter_parameter_logging.rb içinde :email değeri de filtrelenmişler içinde olduğu için log kayıtlarında konulmuyor.
Ama sorarsak konsolda kayıtlı değeri görürüz.
User nesnesinin geçerliliği konusunda çalışacağız ama şimdilik kaydın geçerliliğini sorgulamak için valid? metodu kullanırız, onu bilelim.
Şu ana kadar user nesnesini sadece hafızada tanımlamış olduk, veri tabanına kaydetmek için bir eylem yapmadık. Bu amaçla user nesnesinde save metodu çağrılır.
save metodu başarılı olursa true , olamazsa false döner (Şu anda tüm kaydetme girişimleri başarılı olur, çünkü herhangi bir veri doğrulaması yapılmıyor, ama yakın zamanda yapacağız). Girdiğimiz komut sonucu konsola gelen mesajda veri tabanında yapılan SQL işleminin kodu da görülüyor, Rails bu kodları bizim için üretir ve çalıştırır.
user nesnesine şimdi bakarsak daha önce değerleri nil olan id, created_at ve updated_at sütunlarındaki değerlerin save komutuyla beraber otomatik doldurulduğunu görürüz.
id değeri ilk kayıt olduğu için otomatikman 1 olurken created_at ve updated_at değerleri ise kaydı oluşturduğumuz zamanı gösteriyor.
Ürettiğimiz User nesnesinin özniteliklerine noktalı notasyonla ulaşabiliriz.
Daha ileride göreceğiz genel olarak bir kaydı veri tabanına eklerken yukarıda yaptığımız gibi iki adımda yapmak tavsiye edilir. Ama istersek create metodu kullanarak iki adımı bir arada gerçekleştirebiliriz.
Kaydı silmek için destroy metodu kullanırız.
Veri tabanından bu kayıt silinmiş olmasına rağmen hala hafızada duruyor.
User nesnelerini bulmak
Active Record, User tablosu kayıtlarına erişmek için çeşitli imkanlar sunar. Bunlar User sınıfının sınıf metodlarıdır.
find metodu parametresinde verilen id değerine ait kaydı veri tabanı tablosundan bulup getirir.
Bu arada sandbox modda olduğumuz için bir süre geçince kayıtlar veri tabanından otomatik silinecektir, öyle olursa şaşırmayın.
Sildiğimiz 3 numaralı kayda bakalım
Belli bir öznitelik değerine sahip kaydı bulmak için find_by metodu kullanırız.
Tablodaki ilk kayıt için first metodu kullanırız.
Tüm kayıtları bir array olarak all metodu ile okuruz.
User kayıtlarını değiştirmek
Nesneyi değiştirmek kolay , ancak veri tabanındaki kaydı değiştirmek için başka davranılır.
save kullanmazsak veri tabanında kayıt değişmez bunu test için reload ile kaydı veri tabanından geri çağırabiliriz.
Direk olarak update nesnenin metodunu kullanarak da kayıt değiştirebiliriz, ve bunu updated_at özellik değerinden değiştiğini görebiliriz.
update_attribute metodu ise bir tek özelliği değiştirmek için kullanılır.
Rails konsolda exit yazıp çıkalım.
User kayıt doğrulamaları
Ürettiğimi User modelimiz name ve email öznitelikleri ile çalışmakta, ancak çok sıradan şekilde. Herhangi bir string değeri bu özniteliklere girsek yeterli oluyor. Ama bundan çok daha farklı olmalı örneğin name değeri boş bir string olmamalı ve email değeri bir e-mail adresi formatına sahip olmalı. Dahası ileride email değerini sisteme giriş yapmak için kullanacağımızdan tabloda aynı email değerine sahip tek bir kayıt olmalı.
Kısacası name ve email değerleri öyle sıradan bir string olamaz ve bunları sisteme kaydetmeden önce kontrol etmeliyiz. Active Record bize validations adı verilen işlemlerle bu kontrolleri yapma imkanı sunar. En genel kontroller, presence, length, format ve uniqueness.
Geçerlilik testi
Daha önce de TDD (test driven development) tekniği kullanmıştık. TDD'nin en anlamlı olduğu yerlerden biri bu geçerlilik testleridir. Yazdığımız geçerlilik kurallarının çalıştığını görmenin en iyi yolu geçerli olmayan değerlerle kayıt yapmayı deneyen testler yazmaktır.
Öncelikle geçerli bir User kaydını test eden bir rutinle başlayalım. Şu anda test dosyamızın içinde bir test rutini yok.
test/models/user_test.rb
Öncelikle setup metodu içinde geçerli bir @user nesnesi tanımlayacağız, sonra test bloğunda bu kaydın geçerliliğini test edip testten geçmesini bekleyeceğiz.
Burada assert metodu beklentimizi bildiriyor, yani @user.valid? işlemi true değer dönerse bu test başarılıdır. Test'i çalıştıralım.
1 test çalışmış 1 beklenti varmış, hepsi geçmiş, hata olmamış. Sadece modelleri test etmek için rails test:models komutu kullandık.
Presence geçerlilik kuralı
En temel geçerlilik kuralı presence , özelliğe bir değer girildiğini kontrol eder. Örneğin bu bölümde name ve email değerlerinin verildiğini kontrol edeceğiz. Bu amaçla geçmeyen bir test yazacağız Sonra da değerin girilmesini zorunlu hale getirmek için modele ilave yapacağız.
Şu anda yeni kayıt açarken name değeri vermemizi ya da boş bir string vermemizi engelleyen bir şey yok. Yazacağımız ikinci testte boşluklardan oluşan bir değerin geçmemesini beklenti olarak bildireceğiz.
test/models/user_test.rb
name değeri boşluk olarak verilince testten geçmesin diye assert_not ile beklentimizi ters çeviriyoruz. Şimdi test edelim.
UserTest#test_name_değeri_verilmeli ifadesi bize yazdığımız test kodunun neresinde geçmediğini belirtiyor, bizim test için verdiğimiz konu başlığını aralara alt çizgi ekleyerek yazmış, demek metod gibi kullanıyor.
Şimdi modelimizin kodunda validates metodu kullanarak bir geçerlilik kuralı oluşturalım.
app/models/user.rb
validates metodu birinci parametresi geçerlilik kuralı yazılacak öznitelik adı, ikincisi ise istenen geçerlilik kuralı. Burada presence: true ile name özniteliğine değer girilmesi gerektiği (presence=mevcut) bildiriliyor. Şimdi konsolu açıp name değeri olmayan bir kayıt oluşturup geçerliliğini kontrol edelim, bakalım ne olacak.
Gördüğümüz gibi geçerli bir kayıt değil. valid? metodu ile user nesnesinin geçerliliğini kontrol edince false değer dönerek bize geçerli olmadığını bildiriyor. Uygulamamız çalışırken bize Rails tarafından verilecek mesajı görmek istersek.
İsim boş olamaz diyor. Aslında mesaj bize Rails'in bir özelliğin mevcut olmasını blank? metodu ile kontrol ettiğini gösteriyor. Geçerli bir kayıt olmadığı için veri tabanına kayıt etmeye kalkarsak başarısız olacaktır.
Konsoldan çıkıp bir de test çalıştıralım.
Başarılı bir test oldu demek ki doğru yere müdahale ettik. Şimdi aynı adımları email özelliği için de uygulayalım.
test/models/user_test.rb
ve model
app/models/user.rb
Test edersek başarılı sonuç almalıyız.
Length geçerlilik kuralı
İsim ve e-mailllerin karakter sayıları iki bakımdan sınırlanması gerekiyor. Birincisi ismi sayfamızda yayınlayacağız ve görsel açıdan saçma olmasın diye örneğin maksimum 50 karakterle name öznitelik değerini sınırlamalıyız. email değerini sayfada göstermeyecek olsak bile birçok veri tabanı için maksimum string uzunluğu olan 255 karakterden uzun olmamasını sağlamalıyız.
Öncelikle bu iki sınırı aşan değerlerin geçmemesi gerektiğini test kodlarımıza ekleyelim.
test/models/user_test.rb
Burada string değerde yapılan çarpma işlemini bir açıklayalım. Konsolda bunları deneyip görebiliriz.
Burada gördüğümüz length metodu verilen string değerin karakter sayısını bize döner.
Tabii ki test yaparsak başarısız olacaktır, çünkü henüz bu konularda bir geçerlilik kuralı tanımlamadık.
Şimdi modelimize bu iki uzunluk iin de geçerlilik kuralları ekleyelim.
app/models/user.rb
length (uzunluk-karakter sayısı) özelliğinin maximum alt özelliği, maksimum uzunluk geçerliliği için kurala ilave yapıyor
Şeklinde ayrı kurallar da yazabilirdik, ama aynı özellik için olan kuralları tek satırda birleştirmek daha doğru.
Şimdi test yaparsak başarılı olacaktır.
Format geçerlilik kuralı
Bizim name özelliği için geçerlilik kurallarımız basit, boş olmayacak ve 50 karakterden uzun olmayacak. Ancak email özelliği için daha fazlasına ihtiyacımız var. Bir email formatı içinde neler olmalıdır? En azından bir harf ile başlamalı, arkasından harfler ya da nokta ya da alt çizgiler olabilir, arkasından @ karakteri gelir, arkasından harfler ve noktalar gelir, en sonda yine bir harf olmalıdır.
Bu karmaşık şablonu ifade etmek için tabii ki regexp kullanacağız. Bu regexp işleri her zaman kafamı karıştırır, yavaş ilerleyelim. Öncelikle birkaç tane geçerli adres bilgisi tanımlayıp bunların testten geçtiğini bir görelim. Birkaç değer ile test yapabilmek için önce veri kolleksiyonlarını konsolda birkaç deneme ile görelim.
%w[ ] notasyonun kullanılınca Ruby köşeli parantez içindeki kelimeleri boşluklardan ayırıp, her biri bir string olan elemanlardan oluşan bir array üretir. each metodu uygulandığı koleksiyonun her bir elemanı için kendisine verilen kod bloğunu çalıştırır, bloğa iterasyonu yapılan eleman |adres| şeklinde iki bar arasında değişken adı olarak verilir ve blok içinde bu değişken adı ile kullanılabilir.
Öncelikle bu geçerli adreslerin kabul gördüğü bir test yazalım ki, daha sonra geçersizleri kabul etmeyecek olan rutinimizin , bunları kabul etmeye devam edeceğini garanti altına alalım.
test/models/user_test.rb
Beklentimize (assert satırı) bir argüman daha ekledik. Burada bir string enterpolasyonu var. adres.inspect bize adres değerinin Ruby'nin anladığı şekilde ifadesini verir, örneğin.
Bu eklediğimiz ikinci argüman ile biz beklentimizi koleksiyondaki hangi değerin karşılamadığını konsola yazdırmasını sağlıyoruz. Bir test başarısızlığı olursa hangi adres için olduğu da yazılacak.
Şimdi bir test bloğu da geçersiz olmasını istediğimiz email adresleri için yazalım.
Şimdi test yaparsak başarısız olacaktır.
Gördüğümüz gibi geçersiz olan ilk email adresi değeri ile birlikte mesaj veriyor. Koleksiyon değerlerinden bir tanesi bile testi geçemezse test başarısız demektir.
Gelelim geçerlilik kuralına , bir formatta veri isteyen geçerlilik kuralımız şöyle olur.
Regexp (regular expression) stringlerin belirtilen formatla eşleşmesini görmek için kullanılan değer ifadesidir. Şimdi öyle bir regexp ifadesi yazacağız ki , geçerli adreslerle eşleşecek ama geçersiz adreslerle eşleşmeyecek. Tüm email adres standartlarını karşılayan regexp değerleri internette bulabiliriz, ama bunlar çok karmaşık olabiliyor. Biz şimdilik aklımıza gelenlerle bir ifade oluşturalım.
Bu değerin ne anlama geldiğini rubular.com adresinde bakabiliriz. Kısaca sıralayalım
- / / arasında regexp ifadesi yazılır (stringlerdeki tırnaklar gibi bir sınırlayıcı)
- / /i eşleşme yapılırken büyük-küçük harf bakılmayacağını belirtir.
- \A stringin başını yakalar, burada adrres öncesinde boşluk vs olmaması için bunu koyduk
- \z stringin sonunu yakalar, bunu da adres sonunda boşluk vs olmaması için koyduk.
- [ ] arasında o noktada olası karakterler verilir.
- \w herhangi kelime karakteri demektir, yani harf, sayı ya da alt çizgi.
- [\w+-.] herhangi kelime karakteri ya da + ya da - ya da . işareti demek
- [\w+-.]+ üsttekinden yan yana bir sürü olabilir demek
- @ bildiğimiz @ karakteri olacak
- [a-z\d\-.]+ a'dan z'ye harfler ya da bir sayı ya da - ya da . işaretlerinden bir sürü yan yana olabilir demek. Yani @ sonrasında mesela + olamaz.
- \.[a-z]+ en sonda bir nokta olacak ve arkasından gelen bir ya da bir sürü harf olacak demek
Şimdi modelimize bu format geçerlilik kuralını ekleyelim.
app/models/user.rb
Bu regexp ifadesi tam bir email geçerlilik paterni değil ama şimdilik idare eder. Örneğin user@example..com gibi çift nokta yanyana bir email adresi kabul edilmemeli ama bizim verdiğimiz şablon için bu geçerli bir email adresidir.
Birtek olma geçerlilik kuralı
Kullanıcılar giriş yaparlarken isim benzerliği olabilir diye email adreslerini esas kabul edeceğiz. Bu durumda kayıt yapan kişinin email adresinin aynısı Users tablosunda olmamamlıdır. Geçerlilik kurallarında :uniqueness seçeneği bu kontrolü yapar. Ama olay bu kadar basit değil, ilerledikçe göreceğiz.
Öncelikle , daha önce kaydı veri tabanına atmadan önce kontrol yapılıyordu, ama şimdi önce bir kaydı veri tabanına saklayıp sonra aynı email adresiyle ikinci bir kayıt test etmeliyiz. Önce geçerli olmaması beklenen bir test yazalım.
test/models/user_test.rb
Burada @user.dup ile @user nesnesi ile aynı özelliklere sahip yeni bir nesne kopya olarak üretiliyor. @user nesnesindeki kaydı veri tabanına kaydettikten sonra duplicate_user nesnesi aynı email değerine sahip olduğu için artık geçersiz olmalıdır. Bu şekilde test edersek başarısız olacaktır.
Şimdi geçerlilik kuralımıza ilave yapalım.
app/models/user.rb
Daha bitmedi, email adresleri büyük-küçük harften bağımsızdır, yani foo@bar.com, FOO@BAR.COM ve Foo@bAr.CoM aynı email adresleridir. Test koduna şu satırı ekleyelim.
swapcase metodu verilen string'in harflerinin tek tek büyükse küçük , küçükse büyük yapar. Bu durumda kopya kayıttaki email adresi aynı ama harf büyüklükleri farklı olacaktır, yine de testten geçmemesi lazım. Ama şu halde test yaparsak başarısız olur. Şimdi geçerlilik kuralımıza büyük-küçük bakılmaksızın benzersizlik istediğimizi belirtelim.
app/models/user.rb
Kafa karışmasın uniqueness özelliğine blok içinde alt özellikler verince önceki gibi true yazmamıza gerek yok. case_sensitive: false ise büyük küçük harfe bakılmaksızın benzersiz değer olmalı kuralı.
Bu şekilde Rails'e birtek kayıt kuralı yazabiliriz. Bu kontrolün yetersiz olduğuna dair bazı şikayetler oluşmuş, Veri tabanına kayıt için sırada beklerken (yoğun trafikli sitelerde) çift kayıt olma işlemleri ile karşılaşılmış. Örneğin şu yazıda benzer problem anlatılıyor.
Eğer trafiği yüksek bir siteniz olacaksa bu konuda bir araştırma yapmanızı tavsiye ederim. Genelde bu tip konuları çözerken veri tabanında kayıt yapılırken doğrulamalar yapılıyor, o da aslında sebebi tahmin edilmek zorunda kalınan kayıt etme hataları dönme ihtimali var. İşi veri tabanına bıraksak bile bazı veri tabanı motorlarının büyük-küçük harf kayıtlarda sorun yaşaması sebebiyle, en azından şu email adresini kaydetmeden önce küçük harfe çevirelim. Bu amaçla model dosyamızda kayıt öncesi çalışan bir callback fonksiyonu eklemeliyiz.
app/models/user.rb
Kayıt etmeden önce küçük harfe çevireceğimiz için case_sensitive ifadesini kaldırdık. Test rutininde de büyük-küçük farkını denememize artık gerek yok.
test/models/user_test.rb
Şifre eklemek
Kullanıcı adı ve email adresleri için geçerlilik kurallarımızı ekledik. Sırada kullanıcı için gerekli son öznitelik olan güvenli bir şifre eklenmesi var. Temelde kullanıcıdan bir şifre girmesini zorunlu kılacağız (ikinci bir doğrulama koopyası ile) ve şifreyi veri tabanına kaydederken bir hash olarak kaydedeceğiz. Burada hash dediğimiz bizim Ruby'deki hash tipi veri değil, veriyi geri döndürülemez bir hash değere dönüştürmek.
Kullanıcıyı yetkilendirme yöntemi, girdiği şifreyi hash değere dönüştürmek ve veri tabanındaki hash değerle karşılaştırmak. Eğer eşleşirlerse girilen şifre geçerlidir ve kullanıcı yetkilendirilir. Hash değere dönüştürülmüş değerleri karşılaştırmak kullanıcının şifresinin direk olarak veri tabanında saklamadan kullanmamıza imkan sağlar. Yani veri tabanımız başkalarının eline geçse bile şifreler hala güvende olacaktır.
Hash değere dönüştürülmüş şifre
Birçok güvenli şifre mekanizması model dosyasında Rails'in has_secure_password metodunun çağrılması ile gerçekleştirilir.
Bu şekilde model dosyasına eklediğimizde bu metod şu ilave özellikleri kazandırır:
- Güvenli şekilde hash değere dönüştürülmüş password_digest özniteliğini veri tabanına saklama
- Bir çift sanal öznitelik ekler - password ve password_confirmation - bunlar veri tabanına saklanmaz sadece nesne üretilirken kullanılan öznitelikler olarak modelde mevcuttur. Ayrıca bunların girilme zorunluluğu ve eşlenik olmaları zorunluluğu kuralları da eklenir.
- authenticate metodu ile girilen şifre başarılı ise true değilse false değer alınır.
Bu sihirli metodun tek ihtiyacı modelimize ve veri tabanına password_digest özniteliğini eklememizdir. Bu amaçla rails generate komutunu kullanacağız.
Migrasyon dosyamıza bir bakalım.
users tablosuna string olarak password_digest adında bir sütun ekler. Şimdi migrasyonları çalıştıralım.
has_secure_password şifreleri password_digest haline çevirmek için efsane bcrypt rutinini kullanır. Rails uygulamamıza bu fonksiyonu ekelemek için bcrypt gem'ini GemFile dosyamıza eklemeliyiz.
GemFile
Şimdi bundle ile ilave ettiğimiz gem'in uygulamamıza eklenmesini sağlayalım.
Kullanıcı güvenli şifreye sahip mi?
User modelimize password_digest özniteliğini ekledik ve bcrypt gem'i de uygulamamıza dahil ettik. Şimdi modelimize eklemediysek has_secure_password satırını ekleyelim.
app/models/user.rb
Şimdi test yaparsak başarısız olacaktır.
Bunun sebebi has_secure_password metodu modelde password ve password_confirmation sanal özniteliklerini arıyor fakat bizim test setup kısmında verdiğimiz örnek @user nesnesinde bu öznitelik değerlerini girmedik tabii ki.
Bu kısımda password ve password_confirmation özelliklerine değer girmeliyiz.
test/models/user_test.rb
Artık testten geçer. İsterseniz farklı şifre ve onay şifre girerek onların eşlenik olmasını da test edebilirsiniz, ikisinin aynı olmaması durumunda da hata verecektir.
Minimum şifre standartları
Şifreler tahmin edilmesi güç olmalıdır. Bunu sağlamak için birçok geçerlilik testi yapmalıyız, ama olayı basit tutmak için şimdilik boş olmaması ve en az 6 karakter uzunlukta olmasını düşünelim. Bu amaçla test kodumuza iki test daha ekleyelim.
test/models/user_test.rb
Şu anda geçerlik kuralı eklemediğimiz için test başarısız olacaktır. Model dosyamıza bu iki geçerlik kuralını tek satırda ekleyebiliriz.
app/models/user.rb
Bir kullanıcı üretmek ve yetkilendirmek
User modelimizin temel tanımlaması bitti. Şimdi bir kullanıcı tanımlayıp , görsel sayfasını tasarlayacağız. Daha önce has_secure_password metodunu anlatırken uygulamamıza authenticate adında önemli bir metod eklediğinden bahsetmiştik.
Kullanıcının kayıt yapması ve yetkilenmesi için uygulamaya ilaveleri sonraki bölümde yapacağız. Ancak bu bölümde konsolda bir kullanıcı ekleyeceğiz. Bu kullanıcının kayıtlı kalması için konsolu sandbox modunda değil normal olarak açacağız.
İki işlemi aynı anda yapan create metodunu kullanarak örnek kullanıcımızı ekledik. Şimdi gerçekleşen kaydı görmek için SQLite3 veri tabanını SQLite Browser uygulamasında açarsak kaydımızı göreceğiz.
Gördüğünüz üzere password_digest sütununda yazılan değerin bizim verdiğimiz şifre ile alakası yok .
Bu arada kısa bir bilgi verelim herhangi bir anda veri tabanını sıfırlamak ve içini boşaltmak isterseniz şöyle yaparsınız
- Konsoldan çıkın
- rm -f storage/development.sqlite3 komutu ile mevcut veri tabanı dosyasını silin
- rails db:migrate komutu ile tüm migrasyonları tekrar aktif edin (bir kere çalıştırmak yeterli)
- Rails konsolu tekrar çalıştırın
Hash karıştırması yapılmış bu uzun değer aslında "foobar" string'ine karşılık bcrypt tarafından oluşturulan değerdir ve bu değerden "foobar" değerine dönmek imkansız gibi bir şey.
Gelelim authenticate metodunu kullanma tekniğine.
Kullanıcı nesnesinde authenticate metodu çağrılınca argümanda verilen şifre ile veri tabanı kaydındaki password_digest değeri eşleşirse bize kullanıcı nesnesini eşleşmezse false değer dönecektir. Bunu kullanarak ileride uygulamamızda kullanıcıların yetkilendirilmesi işlemlerini yapacağız nasipse.
Bu kısımda öğrendiklerimiz
- Migrasyonlar ile veri tabanı tablolarımızın yapısını değiştirebiliriz
- Active Record sınıfı veri modellerini işlemek için birçok metoda sahiptir
- Active Record geçerlik kuralları girdiğimiz verileri kısıtlamak için kriterler oluşturmak için kullanılır
- Yaygın kullanılan geçerlilik kuralları , presence, length, ve format
- Regexp ifadeler karmaşık ama çok kuvvetlidir
- Veri tabanlarında kayıtlar için kısıtlamalar tanımlanabilir
- Güvenli şifre alt yapısını has_secure_password metodu kullanarak oluşturabiliriz
Bu bölüm burada bitiyor, sonraki bölümde nasipse kayıt olma sayfasını gerçekleştireceğiz. Şimdilik kalın sağlıcakla..
Hiç yorum yok:
Yorum Gönder