2 Nisan 2025 Çarşamba

Rails 7 Denemeler 5

https://ujk-ujk.blogspot.com/2025/04/rails-7-denemeler-5.html
İçindekiler +


    İletişim sayfası

    Uygulamamıza İletişim sayfası ekleyerek devam edeceğiz. Öncelikle test rutinlerini ekleyelim. 

    test/controllers/static_pages_controller_test.rb

    require "test_helper"

    class StaticPagesControllerTest < ActionDispatch::IntegrationTest
      ....

      test "should get contact" do
        get static_pages_contact_url
        assert_response :success
        assert_select "title", "İletişim | Yeni App"
      end

    end


    Tabi ki test yaparsak hata verecektir.

    $ rails t


    Aynı daha önce about eylemi için yaptıklarımızı tekrarlayacağız. Öncelikle yönlendirmesini yapalım.

    config/routes.rb

    Rails.application.routes.draw do
      get "static_pages/home"
      get "static_pages/help"
      get "static_pages/about"
      get "static_pages/contact"
      ....
    end


    Şimdi de kontrolöre eylemi ekleyelim.

    app/controllers/static_pages_controller.rb

    class StaticPagesController < ApplicationController
      def home
      end

      def help
      end

      def about
      end

      def contact
      end
    end


    Şimdi bir de görsel sayfası ekleyelim.

    app/views/static_pages/contact.html.erb

    <% provide(:title, 'İletişim') %>
    <h1>İletişim</h1>
    <p>
     Yeni App uygulamamız için iletişimi konunun anlatıldığı
     <a href="https://ujk-ujk.blogspot.com/2025/03/rails-7-denemeler-1.html">
     blog sayfalarında</a> yorum yaparak bulabilirsiniz.
    </p>


    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. 

      root "static_pages#home"

    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.

    root_path -> '/'
    root_url -> 'http://www.example.com/'

    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. 

      get "static_pages/help"

    bunun yerine 

      get "/help", to: "static_pages#help"

    yapmalıyız. Bu sayede help_path ve help_url değişkenleri oluşur. Hepsi için uygularsak

    config/routes.rb

    Rails.application.routes.draw do
      root "static_pages#home"
      get "/help", to: "static_pages#help"
      get "/about", to: "static_pages#about"
      get "/contact", to: "static_pages#contact"
    .....


    Yönlendirmeler değiştiği için testlerimiz hata verecektir. Onları da bir düzenleyelim.

    static_pages_controller_test.rb

    require "test_helper"

    class StaticPagesControllerTest < ActionDispatch::IntegrationTest
      test "should get home" do
        get root_path
        assert_response :success
        assert_select "title", "Yeni App"
      end

      test "should get help" do
        get help_path
        assert_response :success
        assert_select "title", "Yardım | Yeni App"
      end

      test "should get about" do
        get about_path
        assert_response :success
        assert_select "title", "Hakkımızda | Yeni App"
      end

      test "should get contact" do
        get contact_path
        assert_response :success
        assert_select "title", "İletişim | Yeni App"
      end

    end




    İ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. 

    <%= link_to "Hakkımızda", "#" %>

    yerine

    <%= link_to "Hakkımızda", about_path %>

    yazabiliriz. 

    Kısmi görselimiz _header.html.erb görselinden başlayalım.

    app/views/layouts/_header.html.erb

    <header class="navbar navbar-fixed-top navbar-inverse">
      <div class="container">
        <%= link_to "Yeni App", "#", id: "logo" %>
        <nav>
          <ul class="nav navbar-nav navbar-right">
            <li><%= link_to "Ana Sayfa", root_path %></li>
            <li><%= link_to "Yardım", help_path %></li>
            <li><%= link_to "Giriş Yap", "#" %></li>
          </ul>
        </nav>
      </div>
    </header>


    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

    <footer class="footer">
      <a href="https://rubyonrails.org/">Ruby On Rails</a>
      <nav>
        <ul>
          <li><%= link_to "Hakkımızda", about_path %></li>
          <li><%= link_to "İletişim", contact_path %></li>
          <li><%= link_to "Haberler", "https://rubyonrails.org/blog/",
            target: "_blank" %></li>
        </ul>
      </nav>
    </footer>


    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 integration_test site_layout
          invoke  test_unit
          create    test/integration/site_layout_test.rb

    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

    require "test_helper"

    class SiteLayoutTest < ActionDispatch::IntegrationTest
      test "layout links" do
        get root_path
        assert_template "static_pages/home"
        assert_select "a[href=?]", root_path, count: 2
        assert_select "a[href=?]", help_path
        assert_select "a[href=?]", about_path
        assert_select "a[href=?]", contact_path
      end
    end


    Bu değişikliklerden sonra test çalıştırınca bir hata aldım ama testle alakalı değil 

    $ rails rails t
    # Running:

    Error:
    SiteLayoutTest#test_layout_links:
    NoMethodError: assert_template has been extracted to a gem.
    To continue using it, add `gem "rails-controller-testing"`
    to your Gemfile.

    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

    source "https://rubygems.org"

    # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
    gem "rails", "7.2.2"

    gem "rails-controller-testing"
    gem "bootstrap-sass", "3.4.1"
    gem "sassc-rails", "2.1.2"
    .....

    ve bundle çalıştıralım

    $ bundle

    Gem yüklendikten sonra testimizi tekrar çalıştıralım.

    $ rails t

    Failure:
    Expected exactly 2 elements matching "a[href=\"/\"]", found 1.

    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. 

    <header class="navbar navbar-fixed-top navbar-inverse">
      <div class="container">
        <%= link_to "Yeni App", "#", id: "logo" %>
        <nav>
          <ul class="nav navbar-nav navbar-right">
            <li><%= link_to "Ana Sayfa", root_path %></li>
            <li><%= link_to "Yardım", help_path %></li>
            <li><%= link_to "Giriş Yap", "#" %></li>
          </ul>
        </nav>
      </div>
    </header>

    Logodaki linki unutmuşum,

        <%= link_to "Yeni App", root_path, id: "logo" %>

    olması gerekiyordu, düzeltip test çalıştırdım, sonuç başarılı oldu. Bu test kodundaki satırlara bir bakalım.

        assert_template "static_pages/home"

    İ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. 

        assert_select "a[href=?]", root_path, count: 2

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

    Kod                                           Eşleşen HTML örnek
    ----------------------------------------------------------------------------
    assert_select "div"                           <div>foobar</div>
    assert_select "div", "foobar"                 <div>foobar</div>
    assert_select "div.nav"                       <div class="nav">foobar</div>
    assert_select "div#profile"                   <div id="profile">foobar</div>
    assert_select "div[name=yo]"                  <div name="yo">hey</div>
    assert_select "a[href=?]", "/", count: 1      <a href="/">foo</a>
    assert_select "a[href=?]", "/", text: "foo"   <a href="/">foo</a>


    Sadece entegrasyon testlerinin çalışması için

    $ rails test:integration
    Running 1 tests in a single process (parallelization threshold is 50)
    1 runs, 5 assertions, 0 failures, 0 errors, 0 skips




    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. 

    $ rails generate controller Users new
          create  app/controllers/users_controller.rb
           route  get "users/new"
          invoke  erb
          create    app/views/users
          create    app/views/users/new.html.erb
          invoke  test_unit
          create    test/controllers/users_controller_test.rb
          invoke  helper
          create    app/helpers/users_helper.rb
          invoke    test_unit


    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

    class UsersController < ApplicationController
      def new
      end
    end


    app/views/users/new.html.erb

    <h1>Users#new</h1>
    <p>Find me in app/views/users/new.html.erb</p>


    test/controllers/users_controller_test.rb

    require "test_helper"

    class UsersControllerTest < ActionDispatch::IntegrationTest
      test "should get new" do
        get users_new_url
        assert_response :success
      end
    end


    Bu noktada test çalıştırırsak sorunsuz geçmelidir.

    $ rails t
    # Running:
    ......
    6 runs, 14 assertions, 0 failures, 0 errors, 0 skips




    Signup URL değeri

    Yönlendirme dosyamızda 

      get "users/new"

    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

    Rails.application.routes.draw do
      root "static_pages#home"
      get "/help", to: "static_pages#help"
      get "/about", to: "static_pages#about"
      get "/contact", to: "static_pages#contact"
      get "/signup", to: "users#new"
    ....


    Tabii ki test çakılacak hemen düzenleyelim

    test/controllers/users_controller_test.rb

    require "test_helper"

    class UsersControllerTest < ActionDispatch::IntegrationTest
      test "should get new" do
        get signup_path
        assert_response :success
      end
    end


    Ş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

    <div class="center jumbotron">

      <h1>Yeni Uygulama</h1>
      <h2>Bu uygulama ile Rails
        <a href="https://ujk-ujk.blogspot.com/2025/03/rails-7-denemeler-2.html#Statik%20Sayfalar">
        Statik Sayfaları</a> öğreniyoruz
      </h2>
      <%= link_to "Hemen Katılın!", signup_path,
              class: "btn btn-lg btn-primary" %>
    </div>

    <%= link_to image_tag("rails.svg", alt: "Rails logo", width: 200),
          "https://rubyonrails.org/" %>


    Son olarak Kayıt Ol sayfasına kaba bir görünüm ekleyelim.

    app/views/users/new.html.erb

    <% provide :title, "Kayıt Ol" %>
    <h1>Kayıt Olun</h1>
    <p>Burası yeni kullanıcıların kayıt olma sayfası olacak</p>


    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. 

    class User
      attr_accessor :name, :email
      def initialize(attributes = {})
        @name = attributes[:name]
        @email = attributes[:email]
      end
      def formatted_email
        "#{@name} <#{@email}>"
      end
    end


    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 

    $ rails generate controller Users new

    kullandığımız gibi generate model ile de bir model ekleyebiliriz. 

    $ rails g model User name:string email:string
          invoke  active_record
          create    db/migrate/20250404135314_create_users.rb
          create    app/models/user.rb
          invoke    test_unit
          create      test/models/user_test.rb
          create      test/fixtures/users.yml

    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

    class CreateUsers < ActiveRecord::Migration[7.2]
      def change
        create_table :users do |t|
          t.string :name
          t.string :email

          t.timestamps
        end
      end
    end


    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,

    $ rails db:migrate
    == 20250404135314 CreateUsers: migrating ==================
    -- create_table(:users)
       -> 0.0078s
    == 20250404135314 CreateUsers: migrated (0.0083s) =========

    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ı














    322



    Hiç yorum yok:

    Yorum Gönder